blob: 4f811783b8ce09bd40361064bdb32a1366a7e1a5 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070021import android.appwidget.AppWidgetManager;
22import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
Winson Chungc3f581b2011-12-05 14:56:29 -080027import android.content.Intent.FilterComparison;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.IntentFilter;
Winson Chung81f39eb2011-01-11 18:05:01 -080029import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.pm.ActivityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -040031import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.pm.PackageInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080033import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.pm.ResolveInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080035import android.content.pm.ServiceInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080036import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.content.res.TypedArray;
38import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.net.Uri;
40import android.os.Binder;
41import android.os.Bundle;
Winson Chungc3f581b2011-12-05 14:56:29 -080042import android.os.Handler;
43import android.os.HandlerThread;
Winson Chung81f39eb2011-01-11 18:05:01 -080044import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.os.RemoteException;
46import android.os.SystemClock;
47import android.util.AttributeSet;
Winson Chung16c8d8a2011-01-20 16:19:33 -080048import android.util.Log;
Winson Chung81f39eb2011-01-11 18:05:01 -080049import android.util.Pair;
Joe Onorato8a9b2202010-02-26 18:56:32 -080050import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070051import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.util.Xml;
53import android.widget.RemoteViews;
54
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070055import com.android.internal.appwidget.IAppWidgetHost;
Winson Chung81f39eb2011-01-11 18:05:01 -080056import com.android.internal.appwidget.IAppWidgetService;
Adam Cohen97300312011-10-12 15:48:13 -070057import com.android.internal.os.AtomicFile;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080058import com.android.internal.util.FastXmlSerializer;
Winson Chung81f39eb2011-01-11 18:05:01 -080059import com.android.internal.widget.IRemoteViewsAdapterConnection;
Winson Chung84bbb022011-02-21 13:57:45 -080060import com.android.internal.widget.IRemoteViewsFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
Adam Cohen97300312011-10-12 15:48:13 -070062import org.xmlpull.v1.XmlPullParser;
63import org.xmlpull.v1.XmlPullParserException;
64import org.xmlpull.v1.XmlSerializer;
65
66import java.io.File;
67import java.io.FileDescriptor;
68import java.io.FileInputStream;
69import java.io.FileNotFoundException;
70import java.io.FileOutputStream;
71import java.io.IOException;
72import java.io.PrintWriter;
73import java.util.ArrayList;
74import java.util.HashMap;
75import java.util.HashSet;
76import java.util.Iterator;
77import java.util.List;
78import java.util.Locale;
Winson Chungc3f581b2011-12-05 14:56:29 -080079import java.util.Set;
Adam Cohen97300312011-10-12 15:48:13 -070080
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070081class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070083 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070085 private static final String SETTINGS_FILENAME = "appwidgets.xml";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070086 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087
88 /*
89 * When identifying a Host or Provider based on the calling process, use the uid field.
90 * When identifying a Host or Provider based on a package manager broadcast, use the
91 * package given.
92 */
93
94 static class Provider {
95 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070096 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070097 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 PendingIntent broadcast;
99 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
100
101 int tag; // for use while saving state (the index)
102 }
103
104 static class Host {
105 int uid;
106 int hostId;
107 String packageName;
Romain Guya5475592009-07-01 17:20:08 -0700108 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700109 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
111
112 int tag; // for use while saving state (the index)
113 }
114
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700115 static class AppWidgetId {
116 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 Provider provider;
118 RemoteViews views;
119 Host host;
120 }
121
Winson Chung81f39eb2011-01-11 18:05:01 -0800122 /**
123 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
124 * This needs to be a static inner class since a reference to the ServiceConnection is held
125 * globally and may lead us to leak AppWidgetService instances (if there were more than one).
126 */
127 static class ServiceConnectionProxy implements ServiceConnection {
Winson Chung81f39eb2011-01-11 18:05:01 -0800128 private final Pair<Integer, Intent.FilterComparison> mKey;
129 private final IBinder mConnectionCb;
130
Winson Chung16c8d8a2011-01-20 16:19:33 -0800131 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800132 mKey = key;
133 mConnectionCb = connectionCb;
134 }
135 public void onServiceConnected(ComponentName name, IBinder service) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800136 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800137 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
138 try {
139 cb.onServiceConnected(service);
Adam Cohenc2be22c2011-03-16 16:33:53 -0700140 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800141 e.printStackTrace();
142 }
143 }
144 public void onServiceDisconnected(ComponentName name) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800145 disconnect();
146 }
147 public void disconnect() {
148 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800149 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
150 try {
151 cb.onServiceDisconnected();
Adam Cohenc2be22c2011-03-16 16:33:53 -0700152 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800153 e.printStackTrace();
154 }
155 }
156 }
157
Winson Chung84bbb022011-02-21 13:57:45 -0800158 // Manages active connections to RemoteViewsServices
Winson Chung81f39eb2011-01-11 18:05:01 -0800159 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
160 mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
Winson Chung84bbb022011-02-21 13:57:45 -0800161 // Manages persistent references to RemoteViewsServices from different App Widgets
162 private final HashMap<FilterComparison, HashSet<Integer>>
163 mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
Winson Chung81f39eb2011-01-11 18:05:01 -0800164
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700166 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 PackageManager mPackageManager;
168 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700169 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700170 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700171 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
172 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 boolean mSafeMode;
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700174 boolean mStateLoaded;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
Adam Cohen7bb98832011-10-05 18:10:13 -0700176 // These are for debugging only -- widgets are going missing in some rare instances
177 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
178 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
179
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700180 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 mContext = context;
182 mPackageManager = context.getPackageManager();
183 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
184 }
185
186 public void systemReady(boolean safeMode) {
187 mSafeMode = safeMode;
188
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700189 synchronized (mAppWidgetIds) {
190 ensureStateLoadedLocked();
191 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192
193 // Register for the boot completed broadcast, so we can send the
194 // ENABLE broacasts. If we try to send them now, they time out,
195 // because the system isn't ready to handle them yet.
196 mContext.registerReceiver(mBroadcastReceiver,
197 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
198
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700199 // Register for configuration changes so we can update the names
200 // of the widgets when the locale changes.
201 mContext.registerReceiver(mBroadcastReceiver,
202 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 // Register for broadcasts about package install, etc., so we can
205 // update the provider list.
206 IntentFilter filter = new IntentFilter();
207 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
Joe Onoratod070e892011-01-07 20:50:37 -0800208 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
210 filter.addDataScheme("package");
211 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800212 // Register for events related to sdcard installation.
213 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800214 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
215 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800216 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 }
218
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700219 private void ensureStateLoadedLocked() {
220 if (!mStateLoaded) {
221 loadAppWidgetList();
222 loadStateLocked();
223 mStateLoaded = true;
224 }
225 }
226
Adam Cohen7bb98832011-10-05 18:10:13 -0700227 private void dumpProvider(Provider p, int index, PrintWriter pw) {
228 AppWidgetProviderInfo info = p.info;
229 pw.print(" ["); pw.print(index); pw.print("] provider ");
230 pw.print(info.provider.flattenToShortString());
231 pw.println(':');
232 pw.print(" min=("); pw.print(info.minWidth);
233 pw.print("x"); pw.print(info.minHeight);
234 pw.print(") minResize=("); pw.print(info.minResizeWidth);
235 pw.print("x"); pw.print(info.minResizeHeight);
236 pw.print(") updatePeriodMillis=");
237 pw.print(info.updatePeriodMillis);
238 pw.print(" resizeMode=");
239 pw.print(info.resizeMode);
240 pw.print(" autoAdvanceViewId=");
241 pw.print(info.autoAdvanceViewId);
242 pw.print(" initialLayout=#");
243 pw.print(Integer.toHexString(info.initialLayout));
244 pw.print(" zombie="); pw.println(p.zombie);
245 }
246
247 private void dumpHost(Host host, int index, PrintWriter pw) {
248 pw.print(" ["); pw.print(index); pw.print("] hostId=");
249 pw.print(host.hostId); pw.print(' ');
250 pw.print(host.packageName); pw.print('/');
251 pw.print(host.uid); pw.println(':');
252 pw.print(" callbacks="); pw.println(host.callbacks);
253 pw.print(" instances.size="); pw.print(host.instances.size());
254 pw.print(" zombie="); pw.println(host.zombie);
255 }
256
257 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
258 pw.print(" ["); pw.print(index); pw.print("] id=");
259 pw.println(id.appWidgetId);
260 pw.print(" hostId=");
261 pw.print(id.host.hostId); pw.print(' ');
262 pw.print(id.host.packageName); pw.print('/');
263 pw.println(id.host.uid);
264 if (id.provider != null) {
265 pw.print(" provider=");
266 pw.println(id.provider.info.provider.flattenToShortString());
267 }
268 if (id.host != null) {
269 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
270 }
271 if (id.views != null) {
272 pw.print(" views="); pw.println(id.views);
273 }
274 }
275
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 @Override
277 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
278 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
279 != PackageManager.PERMISSION_GRANTED) {
280 pw.println("Permission Denial: can't dump from from pid="
281 + Binder.getCallingPid()
282 + ", uid=" + Binder.getCallingUid());
283 return;
284 }
285
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700286 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700288 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700290 dumpProvider(mInstalledProviders.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
292
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700293 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700294 pw.println(" ");
295 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700297 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 }
299
300 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700301 pw.println(" ");
302 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700304 dumpHost(mHosts.get(i), i, pw);
305 }
306
307 N = mDeletedProviders.size();
308 pw.println(" ");
309 pw.println("Deleted Providers:");
310 for (int i=0; i<N; i++) {
311 dumpProvider(mDeletedProviders.get(i), i, pw);
312 }
313
314 N = mDeletedHosts.size();
315 pw.println(" ");
316 pw.println("Deleted Hosts:");
317 for (int i=0; i<N; i++) {
318 dumpHost(mDeletedHosts.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 }
320 }
321 }
322
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700323 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700325 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700326 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700327 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328
329 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
330
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700331 AppWidgetId id = new AppWidgetId();
332 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 id.host = host;
334
335 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700336 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337
338 saveStateLocked();
339
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700340 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
342 }
343
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700344 public void deleteAppWidgetId(int appWidgetId) {
345 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700346 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700347 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700349 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 saveStateLocked();
351 }
352 }
353 }
354
355 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700356 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700357 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 int callingUid = getCallingUid();
359 Host host = lookupHostLocked(callingUid, hostId);
360 if (host != null) {
361 deleteHostLocked(host);
362 saveStateLocked();
363 }
364 }
365 }
366
367 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700368 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700369 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 int callingUid = getCallingUid();
371 final int N = mHosts.size();
372 boolean changed = false;
373 for (int i=N-1; i>=0; i--) {
374 Host host = mHosts.get(i);
375 if (host.uid == callingUid) {
376 deleteHostLocked(host);
377 changed = true;
378 }
379 }
380 if (changed) {
381 saveStateLocked();
382 }
383 }
384 }
385
386 void deleteHostLocked(Host host) {
387 final int N = host.instances.size();
388 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700389 AppWidgetId id = host.instances.get(i);
390 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 }
392 host.instances.clear();
393 mHosts.remove(host);
Adam Cohen7bb98832011-10-05 18:10:13 -0700394 mDeletedHosts.add(host);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 // it's gone or going away, abruptly drop the callback connection
396 host.callbacks = null;
397 }
398
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700399 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800400 // We first unbind all services that are bound to this id
401 unbindAppWidgetRemoteViewsServicesLocked(id);
402
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 Host host = id.host;
404 host.instances.remove(id);
405 pruneHostLocked(host);
406
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700407 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408
409 Provider p = id.provider;
410 if (p != null) {
411 p.instances.remove(id);
412 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700413 // send the broacast saying that this appWidgetId has been deleted
414 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700416 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 mContext.sendBroadcast(intent);
418 if (p.instances.size() == 0) {
419 // cancel the future updates
420 cancelBroadcasts(p);
421
422 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700423 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 intent.setComponent(p.info.provider);
425 mContext.sendBroadcast(intent);
426 }
427 }
428 }
429 }
430
431 void cancelBroadcasts(Provider p) {
432 if (p.broadcast != null) {
433 mAlarmManager.cancel(p.broadcast);
434 long token = Binder.clearCallingIdentity();
435 try {
436 p.broadcast.cancel();
437 } finally {
438 Binder.restoreCallingIdentity(token);
439 }
440 p.broadcast = null;
441 }
442 }
443
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700444 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
445 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
446 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
Dianne Hackborn4912f162011-06-15 17:40:44 -0700447
448 final long ident = Binder.clearCallingIdentity();
449 try {
450 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700451 ensureStateLoadedLocked();
Dianne Hackborn4912f162011-06-15 17:40:44 -0700452 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
453 if (id == null) {
454 throw new IllegalArgumentException("bad appWidgetId");
455 }
456 if (id.provider != null) {
457 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
458 + id.provider.info.provider);
459 }
460 Provider p = lookupProviderLocked(provider);
461 if (p == null) {
462 throw new IllegalArgumentException("not a appwidget provider: " + provider);
463 }
464 if (p.zombie) {
465 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
466 + " safe mode: " + provider);
467 }
468
469 id.provider = p;
470 p.instances.add(id);
471 int instancesSize = p.instances.size();
472 if (instancesSize == 1) {
473 // tell the provider that it's ready
474 sendEnableIntentLocked(p);
475 }
476
477 // send an update now -- We need this update now, and just for this appWidgetId.
478 // It's less critical when the next one happens, so when we schdule the next one,
479 // we add updatePeriodMillis to its start time. That time will have some slop,
480 // but that's okay.
481 sendUpdateIntentLocked(p, new int[] { appWidgetId });
482
483 // schedule the future updates
484 registerForBroadcastsLocked(p, getAppWidgetIds(p));
485 saveStateLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
Dianne Hackborn4912f162011-06-15 17:40:44 -0700487 } finally {
488 Binder.restoreCallingIdentity(ident);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 }
490 }
491
Winson Chung84bbb022011-02-21 13:57:45 -0800492 // Binds to a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800493 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
494 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700495 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800496 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
497 if (id == null) {
498 throw new IllegalArgumentException("bad appWidgetId");
499 }
500 final ComponentName componentName = intent.getComponent();
501 try {
502 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
503 PackageManager.GET_PERMISSIONS);
504 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
505 throw new SecurityException("Selected service does not require "
506 + android.Manifest.permission.BIND_REMOTEVIEWS
507 + ": " + componentName);
508 }
509 } catch (PackageManager.NameNotFoundException e) {
510 throw new IllegalArgumentException("Unknown component " + componentName);
511 }
512
Winson Chung16c8d8a2011-01-20 16:19:33 -0800513 // If there is already a connection made for this service intent, then disconnect from
514 // that first. (This does not allow multiple connections to the same service under
515 // the same key)
516 ServiceConnectionProxy conn = null;
Winson Chung84bbb022011-02-21 13:57:45 -0800517 FilterComparison fc = new FilterComparison(intent);
518 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800519 if (mBoundRemoteViewsServices.containsKey(key)) {
520 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
521 conn.disconnect();
522 mContext.unbindService(conn);
523 mBoundRemoteViewsServices.remove(key);
524 }
525
526 // Bind to the RemoteViewsService (which will trigger a callback to the
527 // RemoteViewsAdapter.onServiceConnected())
Winson Chung81f39eb2011-01-11 18:05:01 -0800528 final long token = Binder.clearCallingIdentity();
529 try {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800530 conn = new ServiceConnectionProxy(key, connection);
Winson Chung81f39eb2011-01-11 18:05:01 -0800531 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
532 mBoundRemoteViewsServices.put(key, conn);
533 } finally {
534 Binder.restoreCallingIdentity(token);
535 }
Winson Chung84bbb022011-02-21 13:57:45 -0800536
537 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
538 // when we can call back to the RemoteViewsService later to destroy associated
539 // factories.
Winson Chung22bc69d2011-02-25 15:13:38 -0800540 incrementAppWidgetServiceRefCount(appWidgetId, fc);
Winson Chung81f39eb2011-01-11 18:05:01 -0800541 }
542 }
543
Winson Chung84bbb022011-02-21 13:57:45 -0800544 // Unbinds from a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800545 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
546 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700547 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800548 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
549 // RemoteViewsAdapter)
550 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
551 new FilterComparison(intent));
552 if (mBoundRemoteViewsServices.containsKey(key)) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800553 // We don't need to use the appWidgetId until after we are sure there is something
554 // to unbind. Note that this may mask certain issues with apps calling unbind()
555 // more than necessary.
556 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
557 if (id == null) {
558 throw new IllegalArgumentException("bad appWidgetId");
559 }
560
561 ServiceConnectionProxy conn =
562 (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
563 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800564 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800565 mBoundRemoteViewsServices.remove(key);
566 } else {
567 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
Winson Chung81f39eb2011-01-11 18:05:01 -0800568 }
569 }
570 }
571
Winson Chung84bbb022011-02-21 13:57:45 -0800572 // Unbinds from a RemoteViewsService when we delete an app widget
Winson Chung81f39eb2011-01-11 18:05:01 -0800573 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800574 int appWidgetId = id.appWidgetId;
575 // Unbind all connections to Services bound to this AppWidgetId
Winson Chung81f39eb2011-01-11 18:05:01 -0800576 Iterator<Pair<Integer, Intent.FilterComparison>> it =
577 mBoundRemoteViewsServices.keySet().iterator();
Winson Chung81f39eb2011-01-11 18:05:01 -0800578 while (it.hasNext()) {
579 final Pair<Integer, Intent.FilterComparison> key = it.next();
580 if (key.first.intValue() == appWidgetId) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800581 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
582 mBoundRemoteViewsServices.get(key);
583 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800584 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800585 it.remove();
Winson Chung81f39eb2011-01-11 18:05:01 -0800586 }
587 }
Winson Chung84bbb022011-02-21 13:57:45 -0800588
589 // Check if we need to destroy any services (if no other app widgets are
590 // referencing the same service)
591 decrementAppWidgetServiceRefCount(appWidgetId);
592 }
593
594 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
595 private void destroyRemoteViewsService(final Intent intent) {
596 final ServiceConnection conn = new ServiceConnection() {
597 @Override
598 public void onServiceConnected(ComponentName name, IBinder service) {
599 final IRemoteViewsFactory cb =
600 IRemoteViewsFactory.Stub.asInterface(service);
601 try {
602 cb.onDestroy(intent);
Adam Cohen2625fea2011-03-23 17:24:30 -0700603 } catch (RemoteException e) {
604 e.printStackTrace();
605 } catch (RuntimeException e) {
Winson Chung84bbb022011-02-21 13:57:45 -0800606 e.printStackTrace();
607 }
608 mContext.unbindService(this);
609 }
610 @Override
611 public void onServiceDisconnected(android.content.ComponentName name) {
612 // Do nothing
613 }
614 };
615
616 // Bind to the service and remove the static intent->factory mapping in the
617 // RemoteViewsService.
618 final long token = Binder.clearCallingIdentity();
619 try {
620 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
621 } finally {
622 Binder.restoreCallingIdentity(token);
623 }
624 }
625
626 // Adds to the ref-count for a given RemoteViewsService intent
627 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
628 HashSet<Integer> appWidgetIds = null;
629 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
630 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
631 } else {
632 appWidgetIds = new HashSet<Integer>();
633 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
634 }
635 appWidgetIds.add(appWidgetId);
636 }
637
638 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
639 // the ref-count reaches zero.
640 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
641 Iterator<FilterComparison> it =
642 mRemoteViewsServicesAppWidgets.keySet().iterator();
643 while (it.hasNext()) {
644 final FilterComparison key = it.next();
645 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
646 if (ids.remove(appWidgetId)) {
647 // If we have removed the last app widget referencing this service, then we
648 // should destroy it and remove it from this set
649 if (ids.isEmpty()) {
650 destroyRemoteViewsService(key.getIntent());
651 it.remove();
652 }
653 }
654 }
Winson Chung81f39eb2011-01-11 18:05:01 -0800655 }
656
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700657 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
658 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700659 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700660 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 if (id != null && id.provider != null && !id.provider.zombie) {
662 return id.provider.info;
663 }
664 return null;
665 }
666 }
667
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700668 public RemoteViews getAppWidgetViews(int appWidgetId) {
669 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700670 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700671 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 if (id != null) {
673 return id.views;
674 }
675 return null;
676 }
677 }
678
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700679 public List<AppWidgetProviderInfo> getInstalledProviders() {
680 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700681 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700683 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 for (int i=0; i<N; i++) {
685 Provider p = mInstalledProviders.get(i);
686 if (!p.zombie) {
687 result.add(p.info);
688 }
689 }
690 return result;
691 }
692 }
693
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700694 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
695 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 return;
697 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700698 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 return;
700 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700701 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700703 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700704 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700706 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
707 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 }
709 }
710 }
711
Adam Cohen2dd21972010-08-15 18:20:04 -0700712 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
713 if (appWidgetIds == null) {
714 return;
715 }
716 if (appWidgetIds.length == 0) {
717 return;
718 }
719 final int N = appWidgetIds.length;
720
721 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700722 ensureStateLoadedLocked();
Adam Cohen2dd21972010-08-15 18:20:04 -0700723 for (int i=0; i<N; i++) {
724 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
725 updateAppWidgetInstanceLocked(id, views, true);
726 }
727 }
728 }
729
Winson Chung6394c0e2010-08-16 10:14:56 -0700730 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700731 if (appWidgetIds == null) {
732 return;
733 }
734 if (appWidgetIds.length == 0) {
735 return;
736 }
737 final int N = appWidgetIds.length;
738
739 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700740 ensureStateLoadedLocked();
Winson Chung499cb9f2010-07-16 11:18:17 -0700741 for (int i=0; i<N; i++) {
742 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700743 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700744 }
745 }
746 }
747
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700748 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
749 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700750 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 Provider p = lookupProviderLocked(provider);
752 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800753 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 return;
755 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700756 ArrayList<AppWidgetId> instances = p.instances;
Jeff Sharkey933deca2011-11-03 11:26:11 -0700757 final int callingUid = getCallingUid();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 final int N = instances.size();
759 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700760 AppWidgetId id = instances.get(i);
Jeff Sharkey933deca2011-11-03 11:26:11 -0700761 if (canAccessAppWidgetId(id, callingUid)) {
762 updateAppWidgetInstanceLocked(id, views);
763 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 }
765 }
766 }
767
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700768 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700769 updateAppWidgetInstanceLocked(id, views, false);
770 }
771
772 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700773 // allow for stale appWidgetIds and other badness
774 // lookup also checks that the calling process can access the appWidgetId
775 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700777
778 // We do not want to save this RemoteViews
779 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780
781 // is anyone listening?
782 if (id.host.callbacks != null) {
783 try {
784 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700785 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 } catch (RemoteException e) {
787 // It failed; remove the callback. No need to prune because
788 // we know that this host is still referenced by this instance.
789 id.host.callbacks = null;
790 }
791 }
792 }
793 }
794
Winson Chung6394c0e2010-08-16 10:14:56 -0700795 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700796 // allow for stale appWidgetIds and other badness
797 // lookup also checks that the calling process can access the appWidgetId
798 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
799 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700800 // is anyone listening?
801 if (id.host.callbacks != null) {
802 try {
803 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700804 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700805 } catch (RemoteException e) {
806 // It failed; remove the callback. No need to prune because
807 // we know that this host is still referenced by this instance.
808 id.host.callbacks = null;
809 }
810 }
Winson Chungc3f581b2011-12-05 14:56:29 -0800811
812 // If the host is unavailable, then we call the associated
813 // RemoteViewsFactory.onDataSetChanged() directly
814 if (id.host.callbacks == null) {
815 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
816 for (FilterComparison key : keys) {
817 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
818 Intent intent = key.getIntent();
819
820 final ServiceConnection conn = new ServiceConnection() {
821 @Override
822 public void onServiceConnected(ComponentName name, IBinder service) {
823 IRemoteViewsFactory cb =
824 IRemoteViewsFactory.Stub.asInterface(service);
825 try {
826 cb.onDataSetChangedAsync();
827 } catch (RemoteException e) {
828 e.printStackTrace();
829 } catch (RuntimeException e) {
830 e.printStackTrace();
831 }
832 mContext.unbindService(this);
833 }
834 @Override
835 public void onServiceDisconnected(android.content.ComponentName name) {
836 // Do nothing
837 }
838 };
839
840 // Bind to the service and call onDataSetChanged()
841 final long token = Binder.clearCallingIdentity();
842 try {
843 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
844 } finally {
845 Binder.restoreCallingIdentity(token);
846 }
847 }
848 }
849 }
Winson Chung499cb9f2010-07-16 11:18:17 -0700850 }
851 }
852
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700853 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 List<RemoteViews> updatedViews) {
855 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700856 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700857 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
859 host.callbacks = callbacks;
860
861 updatedViews.clear();
862
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700863 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 int N = instances.size();
865 int[] updatedIds = new int[N];
866 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700867 AppWidgetId id = instances.get(i);
868 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 updatedViews.add(id.views);
870 }
871 return updatedIds;
872 }
873 }
874
875 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700876 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700877 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700879 if (host != null) {
880 host.callbacks = null;
881 pruneHostLocked(host);
882 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 }
884 }
885
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700886 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700888 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 return true;
890 }
891 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700892 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 return true;
894 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700895 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700897 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 return true;
899 }
900 // Nobody else can access it.
901 return false;
902 }
903
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700904 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700906 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700908 AppWidgetId id = mAppWidgetIds.get(i);
909 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 return id;
911 }
912 }
913 return null;
914 }
915
916 Provider lookupProviderLocked(ComponentName provider) {
917 final int N = mInstalledProviders.size();
918 for (int i=0; i<N; i++) {
919 Provider p = mInstalledProviders.get(i);
Adam Cohenbac26a122011-08-16 20:42:30 -0700920 if (p.info.provider.equals(provider)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 return p;
922 }
923 }
924 return null;
925 }
926
927 Host lookupHostLocked(int uid, int hostId) {
928 final int N = mHosts.size();
929 for (int i=0; i<N; i++) {
930 Host h = mHosts.get(i);
931 if (h.uid == uid && h.hostId == hostId) {
932 return h;
933 }
934 }
935 return null;
936 }
937
938 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
939 final int N = mHosts.size();
940 for (int i=0; i<N; i++) {
941 Host h = mHosts.get(i);
942 if (h.hostId == hostId && h.packageName.equals(packageName)) {
943 return h;
944 }
945 }
946 Host host = new Host();
947 host.packageName = packageName;
948 host.uid = uid;
949 host.hostId = hostId;
950 mHosts.add(host);
951 return host;
952 }
953
954 void pruneHostLocked(Host host) {
955 if (host.instances.size() == 0 && host.callbacks == null) {
956 mHosts.remove(host);
957 }
958 }
959
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700960 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 PackageManager pm = mPackageManager;
962
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700963 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
965 PackageManager.GET_META_DATA);
966
Bjorn Bringert5f857802010-02-10 23:09:48 +0000967 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 for (int i=0; i<N; i++) {
969 ResolveInfo ri = broadcastReceivers.get(i);
970 addProviderLocked(ri);
971 }
972 }
973
974 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800975 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
976 return false;
977 }
978 if (!ri.activityInfo.isEnabled()) {
979 return false;
980 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
982 ri.activityInfo.name), ri);
983 if (p != null) {
984 mInstalledProviders.add(p);
985 return true;
986 } else {
987 return false;
988 }
989 }
990
991 void removeProviderLocked(int index, Provider p) {
992 int N = p.instances.size();
993 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700994 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700996 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 // Stop telling the host about updates for this from now on
998 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700999 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001001 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 id.provider = null;
1003 pruneHostLocked(id.host);
1004 id.host = null;
1005 }
1006 p.instances.clear();
1007 mInstalledProviders.remove(index);
Adam Cohen7bb98832011-10-05 18:10:13 -07001008 mDeletedProviders.add(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1010 cancelBroadcasts(p);
1011 }
1012
1013 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001014 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 intent.setComponent(p.info.provider);
1016 mContext.sendBroadcast(intent);
1017 }
1018
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001019 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1020 if (appWidgetIds != null && appWidgetIds.length > 0) {
1021 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1022 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 intent.setComponent(p.info.provider);
1024 mContext.sendBroadcast(intent);
1025 }
1026 }
1027
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001028 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029 if (p.info.updatePeriodMillis > 0) {
1030 // if this is the first instance, set the alarm. otherwise,
1031 // rely on the fact that we've already set it and that
1032 // PendingIntent.getBroadcast will update the extras.
1033 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001034 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1035 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 intent.setComponent(p.info.provider);
1037 long token = Binder.clearCallingIdentity();
1038 try {
1039 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1040 PendingIntent.FLAG_UPDATE_CURRENT);
1041 } finally {
1042 Binder.restoreCallingIdentity(token);
1043 }
1044 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -07001045 long period = p.info.updatePeriodMillis;
1046 if (period < MIN_UPDATE_PERIOD) {
1047 period = MIN_UPDATE_PERIOD;
1048 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001049 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -07001050 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051 }
1052 }
1053 }
1054
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001055 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001057 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001059 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001061 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 }
1063
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001064 public int[] getAppWidgetIds(ComponentName provider) {
1065 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001066 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 Provider p = lookupProviderLocked(provider);
1068 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001069 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 } else {
1071 return new int[0];
1072 }
1073 }
1074 }
1075
1076 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1077 Provider p = null;
1078
1079 ActivityInfo activityInfo = ri.activityInfo;
1080 XmlResourceParser parser = null;
1081 try {
1082 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001083 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001085 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001086 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 return null;
1088 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001089
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -08001091
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 int type;
1093 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1094 && type != XmlPullParser.START_TAG) {
1095 // drain whitespace, comments, etc.
1096 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001097
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001099 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001100 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001101 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 return null;
1103 }
1104
1105 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001106 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 info.provider = component;
1108 p.uid = activityInfo.applicationInfo.uid;
1109
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001110 Resources res = mPackageManager.getResourcesForApplication(
1111 activityInfo.applicationInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001112
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001113 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001114 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001115
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001116 // These dimensions has to be resolved in the application's context.
1117 // We simply send back the raw complex data, which will be
1118 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1119 TypedValue value = sa.peekValue(
1120 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1121 info.minWidth = value != null ? value.data : 0;
1122 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1123 info.minHeight = value != null ? value.data : 0;
Adam Cohen1bfaf562011-07-19 18:05:33 -07001124 value = sa.peekValue(
1125 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1126 info.minResizeWidth = value != null ? value.data : info.minWidth;
1127 value = sa.peekValue(
1128 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1129 info.minResizeHeight = value != null ? value.data : info.minHeight;
Adam Cohend2e20de2011-02-25 12:03:37 -08001130
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001132 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001134 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001136 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 if (className != null) {
1138 info.configure = new ComponentName(component.getPackageName(), className);
1139 }
1140 info.label = activityInfo.loadLabel(mPackageManager).toString();
1141 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001142 info.previewImage = sa.getResourceId(
1143 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -07001144 info.autoAdvanceViewId = sa.getResourceId(
1145 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Adam Cohen9611f2e2011-02-28 13:39:38 -08001146 info.resizeMode = sa.getInt(
Adam Cohend2e20de2011-02-25 12:03:37 -08001147 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1148 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 sa.recycle();
1151 } catch (Exception e) {
1152 // Ok to catch Exception here, because anything going wrong because
1153 // of what a client process passes to us should not be fatal for the
1154 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001155 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001156 return null;
1157 } finally {
1158 if (parser != null) parser.close();
1159 }
1160 return p;
1161 }
1162
1163 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1164 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1165 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1166 throw new PackageManager.NameNotFoundException();
1167 }
1168 return pkgInfo.applicationInfo.uid;
1169 }
1170
1171 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1172 int callingUid = getCallingUid();
1173 int packageUid;
1174 try {
1175 packageUid = getUidForPackage(packageName);
1176 } catch (PackageManager.NameNotFoundException ex) {
1177 throw new IllegalArgumentException("packageName and uid don't match packageName="
1178 + packageName);
1179 }
Jeff Brown10e89712011-07-08 18:52:57 -07001180 if (callingUid != packageUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 throw new IllegalArgumentException("packageName and uid don't match packageName="
1182 + packageName);
1183 }
1184 return callingUid;
1185 }
1186
1187 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001188 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001189 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 final int N = mInstalledProviders.size();
1191 for (int i=0; i<N; i++) {
1192 Provider p = mInstalledProviders.get(i);
1193 if (p.instances.size() > 0) {
1194 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001195 int[] appWidgetIds = getAppWidgetIds(p);
1196 sendUpdateIntentLocked(p, appWidgetIds);
1197 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198 }
1199 }
1200 }
1201 }
1202
1203 // only call from initialization -- it assumes that the data structures are all empty
1204 void loadStateLocked() {
Adam Cohen97300312011-10-12 15:48:13 -07001205 AtomicFile file = savedStateFile();
1206 try {
1207 FileInputStream stream = file.openRead();
1208 readStateFromFileLocked(stream);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209
Adam Cohen97300312011-10-12 15:48:13 -07001210 if (stream != null) {
1211 try {
1212 stream.close();
1213 } catch (IOException e) {
1214 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1215 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 }
Adam Cohen97300312011-10-12 15:48:13 -07001217 } catch (FileNotFoundException e) {
1218 Slog.w(TAG, "Failed to read state: " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 }
1220 }
Adam Cohen97300312011-10-12 15:48:13 -07001221
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001222 void saveStateLocked() {
Adam Cohen97300312011-10-12 15:48:13 -07001223 AtomicFile file = savedStateFile();
1224 FileOutputStream stream;
1225 try {
1226 stream = file.startWrite();
1227 if (writeStateToFileLocked(stream)) {
1228 file.finishWrite(stream);
1229 } else {
1230 file.failWrite(stream);
1231 Slog.w(TAG, "Failed to save state, restoring backup.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 }
Adam Cohen97300312011-10-12 15:48:13 -07001233 } catch (IOException e) {
1234 Slog.w(TAG, "Failed open state file for write: " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001235 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 }
1237
Adam Cohen97300312011-10-12 15:48:13 -07001238 boolean writeStateToFileLocked(FileOutputStream stream) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239 int N;
1240
1241 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 XmlSerializer out = new FastXmlSerializer();
1243 out.setOutput(stream, "utf-8");
1244 out.startDocument(null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 out.startTag(null, "gs");
1246
1247 int providerIndex = 0;
1248 N = mInstalledProviders.size();
1249 for (int i=0; i<N; i++) {
1250 Provider p = mInstalledProviders.get(i);
1251 if (p.instances.size() > 0) {
1252 out.startTag(null, "p");
1253 out.attribute(null, "pkg", p.info.provider.getPackageName());
1254 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001255 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 p.tag = providerIndex;
1257 providerIndex++;
1258 }
1259 }
1260
1261 N = mHosts.size();
1262 for (int i=0; i<N; i++) {
1263 Host host = mHosts.get(i);
1264 out.startTag(null, "h");
1265 out.attribute(null, "pkg", host.packageName);
1266 out.attribute(null, "id", Integer.toHexString(host.hostId));
1267 out.endTag(null, "h");
1268 host.tag = i;
1269 }
1270
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001271 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001272 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001273 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001275 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001276 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1277 if (id.provider != null) {
1278 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1279 }
1280 out.endTag(null, "g");
1281 }
1282
1283 out.endTag(null, "gs");
1284
1285 out.endDocument();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001286 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 } catch (IOException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001288 Slog.w(TAG, "Failed to write state: " + e);
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001289 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 }
1291 }
1292
Adam Cohen97300312011-10-12 15:48:13 -07001293 void readStateFromFileLocked(FileInputStream stream) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294 boolean success = false;
1295
1296 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001297 XmlPullParser parser = Xml.newPullParser();
1298 parser.setInput(stream, null);
1299
1300 int type;
1301 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001302 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001303 do {
1304 type = parser.next();
1305 if (type == XmlPullParser.START_TAG) {
1306 String tag = parser.getName();
1307 if ("p".equals(tag)) {
1308 // TODO: do we need to check that this package has the same signature
1309 // as before?
1310 String pkg = parser.getAttributeValue(null, "pkg");
1311 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001312
1313 final PackageManager packageManager = mContext.getPackageManager();
1314 try {
1315 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1316 } catch (PackageManager.NameNotFoundException e) {
1317 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1318 new String[] { pkg });
1319 pkg = pkgs[0];
1320 }
1321
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1323 if (p == null && mSafeMode) {
1324 // if we're in safe mode, make a temporary one
1325 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001326 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 p.info.provider = new ComponentName(pkg, cl);
1328 p.zombie = true;
1329 mInstalledProviders.add(p);
1330 }
1331 if (p != null) {
1332 // if it wasn't uninstalled or something
1333 loadedProviders.put(providerIndex, p);
1334 }
1335 providerIndex++;
1336 }
1337 else if ("h".equals(tag)) {
1338 Host host = new Host();
1339
1340 // TODO: do we need to check that this package has the same signature
1341 // as before?
1342 host.packageName = parser.getAttributeValue(null, "pkg");
1343 try {
1344 host.uid = getUidForPackage(host.packageName);
1345 } catch (PackageManager.NameNotFoundException ex) {
1346 host.zombie = true;
1347 }
1348 if (!host.zombie || mSafeMode) {
1349 // In safe mode, we don't discard the hosts we don't recognize
1350 // so that they're not pruned from our list. Otherwise, we do.
1351 host.hostId = Integer.parseInt(
1352 parser.getAttributeValue(null, "id"), 16);
1353 mHosts.add(host);
1354 }
1355 }
1356 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001357 AppWidgetId id = new AppWidgetId();
1358 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1359 if (id.appWidgetId >= mNextAppWidgetId) {
1360 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 }
1362
1363 String providerString = parser.getAttributeValue(null, "p");
1364 if (providerString != null) {
1365 // there's no provider if it hasn't been bound yet.
1366 // maybe we don't have to save this, but it brings the system
1367 // to the state it was in.
1368 int pIndex = Integer.parseInt(providerString, 16);
1369 id.provider = loadedProviders.get(pIndex);
1370 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001371 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 + pIndex + " which is " + id.provider);
1373 }
1374 if (id.provider == null) {
1375 // This provider is gone. We just let the host figure out
1376 // that this happened when it fails to load it.
1377 continue;
1378 }
1379 }
1380
1381 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1382 id.host = mHosts.get(hIndex);
1383 if (id.host == null) {
1384 // This host is gone.
1385 continue;
1386 }
1387
1388 if (id.provider != null) {
1389 id.provider.instances.add(id);
1390 }
1391 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001392 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 }
1394 }
1395 } while (type != XmlPullParser.END_DOCUMENT);
1396 success = true;
1397 } catch (NullPointerException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001398 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 } catch (NumberFormatException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001400 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401 } catch (XmlPullParserException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001402 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 } catch (IOException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001404 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405 } catch (IndexOutOfBoundsException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001406 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 }
1408
1409 if (success) {
1410 // delete any hosts that didn't manage to get connected (should happen)
1411 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001412 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 pruneHostLocked(mHosts.get(i));
1414 }
1415 } else {
1416 // failed reading, clean up
Adam Cohen97300312011-10-12 15:48:13 -07001417 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1418
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001419 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001420 mHosts.clear();
1421 final int N = mInstalledProviders.size();
1422 for (int i=0; i<N; i++) {
1423 mInstalledProviders.get(i).instances.clear();
1424 }
1425 }
1426 }
1427
Adam Cohen97300312011-10-12 15:48:13 -07001428 AtomicFile savedStateFile() {
1429 return new AtomicFile(new File("/data/system/" + SETTINGS_FILENAME));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001430 }
1431
1432 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1433 public void onReceive(Context context, Intent intent) {
1434 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001435 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001436 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1437 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001438 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1439 Locale revised = Locale.getDefault();
1440 if (revised == null || mLocale == null ||
1441 !(revised.equals(mLocale))) {
1442 mLocale = revised;
1443
1444 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001445 ensureStateLoadedLocked();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001446 int N = mInstalledProviders.size();
1447 for (int i=N-1; i>=0; i--) {
1448 Provider p = mInstalledProviders.get(i);
1449 String pkgName = p.info.provider.getPackageName();
1450 updateProvidersForPackageLocked(pkgName);
1451 }
1452 saveStateLocked();
1453 }
1454 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001455 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001456 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001457 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001458 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001459 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001460 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1461 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001462 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001463 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1464 added = false;
1465 } else {
1466 Uri uri = intent.getData();
1467 if (uri == null) {
1468 return;
1469 }
1470 String pkgName = uri.getSchemeSpecificPart();
1471 if (pkgName == null) {
1472 return;
1473 }
1474 pkgList = new String[] { pkgName };
1475 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001476 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001477 }
1478 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 return;
1480 }
Joe Onoratod070e892011-01-07 20:50:37 -08001481 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001482 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001483 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001485 if (changed || (extras != null &&
1486 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001487 for (String pkgName : pkgList) {
1488 // The package was just upgraded
1489 updateProvidersForPackageLocked(pkgName);
1490 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 } else {
1492 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001493 for (String pkgName : pkgList) {
1494 addProvidersForPackageLocked(pkgName);
1495 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 }
1497 saveStateLocked();
1498 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001499 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001500 Bundle extras = intent.getExtras();
1501 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1502 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1503 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001504 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001505 ensureStateLoadedLocked();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001506 for (String pkgName : pkgList) {
1507 removeProvidersForPackageLocked(pkgName);
1508 saveStateLocked();
1509 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 }
1511 }
1512 }
1513 }
1514 }
1515 };
1516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001518 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001519 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001520 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1521 PackageManager.GET_META_DATA);
1522
Bjorn Bringert5f857802010-02-10 23:09:48 +00001523 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524 for (int i=0; i<N; i++) {
1525 ResolveInfo ri = broadcastReceivers.get(i);
1526 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001527 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1528 continue;
1529 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 if (pkgName.equals(ai.packageName)) {
1531 addProviderLocked(ri);
1532 }
1533 }
1534 }
1535
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001537 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001538 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001539 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1541 PackageManager.GET_META_DATA);
1542
1543 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001544 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 for (int i=0; i<N; i++) {
1546 ResolveInfo ri = broadcastReceivers.get(i);
1547 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001548 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1549 continue;
1550 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 if (pkgName.equals(ai.packageName)) {
1552 ComponentName component = new ComponentName(ai.packageName, ai.name);
1553 Provider p = lookupProviderLocked(component);
1554 if (p == null) {
1555 if (addProviderLocked(ri)) {
1556 keep.add(ai.name);
1557 }
1558 } else {
1559 Provider parsed = parseProviderInfoXml(component, ri);
1560 if (parsed != null) {
1561 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001562 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 p.info = parsed.info;
1564 // If it's enabled
1565 final int M = p.instances.size();
1566 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001567 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001568 // Reschedule for the new updatePeriodMillis (don't worry about handling
1569 // it specially if updatePeriodMillis didn't change because we just sent
1570 // an update, and the next one will be updatePeriodMillis from now).
1571 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001572 registerForBroadcastsLocked(p, appWidgetIds);
1573 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001574 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001575 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001576 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001577 if (id.host != null && id.host.callbacks != null) {
1578 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001579 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 } catch (RemoteException ex) {
1581 // It failed; remove the callback. No need to prune because
1582 // we know that this host is still referenced by this
1583 // instance.
1584 id.host.callbacks = null;
1585 }
1586 }
1587 }
1588 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001589 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001590 }
1591 }
1592 }
1593 }
1594 }
1595
1596 // prune the ones we don't want to keep
1597 N = mInstalledProviders.size();
1598 for (int i=N-1; i>=0; i--) {
1599 Provider p = mInstalledProviders.get(i);
1600 if (pkgName.equals(p.info.provider.getPackageName())
1601 && !keep.contains(p.info.provider.getClassName())) {
1602 removeProviderLocked(i, p);
1603 }
1604 }
1605 }
1606
1607 void removeProvidersForPackageLocked(String pkgName) {
1608 int N = mInstalledProviders.size();
1609 for (int i=N-1; i>=0; i--) {
1610 Provider p = mInstalledProviders.get(i);
1611 if (pkgName.equals(p.info.provider.getPackageName())) {
1612 removeProviderLocked(i, p);
1613 }
1614 }
1615
1616 // Delete the hosts for this package too
1617 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001618 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619 // so we don't need to worry about sending DISABLE broadcasts to them.
1620 N = mHosts.size();
1621 for (int i=N-1; i>=0; i--) {
1622 Host host = mHosts.get(i);
1623 if (pkgName.equals(host.packageName)) {
1624 deleteHostLocked(host);
1625 }
1626 }
1627 }
1628}
1629