blob: de5d0acf7482f110a0695d860a09ec829b6362de [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;
27import android.content.IntentFilter;
28import android.content.pm.ActivityInfo;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageInfo;
31import android.content.pm.ResolveInfo;
32import android.content.pm.PackageItemInfo;
33import android.content.res.TypedArray;
34import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.net.Uri;
36import android.os.Binder;
37import android.os.Bundle;
38import android.os.RemoteException;
39import android.os.SystemClock;
40import android.util.AttributeSet;
41import android.util.Log;
42import android.util.Xml;
43import android.widget.RemoteViews;
44
45import java.io.IOException;
46import java.io.File;
47import java.io.FileDescriptor;
48import java.io.FileInputStream;
49import java.io.FileOutputStream;
50import java.io.PrintWriter;
51import java.util.ArrayList;
52import java.util.List;
53import java.util.HashMap;
54import java.util.HashSet;
55
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070056import com.android.internal.appwidget.IAppWidgetService;
57import com.android.internal.appwidget.IAppWidgetHost;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import com.android.internal.util.XmlUtils;
59import com.android.internal.util.FastXmlSerializer;
60
61import org.xmlpull.v1.XmlPullParser;
62import org.xmlpull.v1.XmlPullParserException;
63import org.xmlpull.v1.XmlSerializer;
64
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070065class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070067 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070069 private static final String SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
71
72 /*
73 * When identifying a Host or Provider based on the calling process, use the uid field.
74 * When identifying a Host or Provider based on a package manager broadcast, use the
75 * package given.
76 */
77
78 static class Provider {
79 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070080 AppWidgetProviderInfo info;
81 ArrayList<AppWidgetId> instances = new ArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 PendingIntent broadcast;
83 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
84
85 int tag; // for use while saving state (the index)
86 }
87
88 static class Host {
89 int uid;
90 int hostId;
91 String packageName;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070092 ArrayList<AppWidgetId> instances = new ArrayList();
93 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
95
96 int tag; // for use while saving state (the index)
97 }
98
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070099 static class AppWidgetId {
100 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 Provider provider;
102 RemoteViews views;
103 Host host;
104 }
105
106 Context mContext;
107 PackageManager mPackageManager;
108 AlarmManager mAlarmManager;
109 ArrayList<Provider> mInstalledProviders = new ArrayList();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700110 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
111 ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 ArrayList<Host> mHosts = new ArrayList();
113 boolean mSafeMode;
114
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700115 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 mContext = context;
117 mPackageManager = context.getPackageManager();
118 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
119 }
120
121 public void systemReady(boolean safeMode) {
122 mSafeMode = safeMode;
123
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700124 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 loadStateLocked();
126
127 // Register for the boot completed broadcast, so we can send the
128 // ENABLE broacasts. If we try to send them now, they time out,
129 // because the system isn't ready to handle them yet.
130 mContext.registerReceiver(mBroadcastReceiver,
131 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
132
133 // Register for broadcasts about package install, etc., so we can
134 // update the provider list.
135 IntentFilter filter = new IntentFilter();
136 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
137 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
138 filter.addDataScheme("package");
139 mContext.registerReceiver(mBroadcastReceiver, filter);
140 }
141
142 @Override
143 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
144 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
145 != PackageManager.PERMISSION_GRANTED) {
146 pw.println("Permission Denial: can't dump from from pid="
147 + Binder.getCallingPid()
148 + ", uid=" + Binder.getCallingUid());
149 return;
150 }
151
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700152 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 int N = mInstalledProviders.size();
154 pw.println("Providers: (size=" + N + ")");
155 for (int i=0; i<N; i++) {
156 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700157 AppWidgetProviderInfo info = p.info;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 pw.println(" [" + i + "] provder=" + info.provider
159 + " min=(" + info.minWidth + "x" + info.minHeight + ")"
160 + " updatePeriodMillis=" + info.updatePeriodMillis
161 + " initialLayout=" + info.initialLayout + " zombie=" + p.zombie);
162 }
163
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700164 N = mAppWidgetIds.size();
165 pw.println("AppWidgetIds: (size=" + N + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700167 AppWidgetId id = mAppWidgetIds.get(i);
168 pw.println(" [" + i + "] appWidgetId=" + id.appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 + " host=" + id.host.hostId + "/" + id.host.packageName + " provider="
170 + (id.provider == null ? "null" : id.provider.info.provider)
171 + " host.callbacks=" + (id.host != null ? id.host.callbacks : "(no host)")
172 + " views=" + id.views);
173 }
174
175 N = mHosts.size();
176 pw.println("Hosts: (size=" + N + ")");
177 for (int i=0; i<N; i++) {
178 Host host = mHosts.get(i);
179 pw.println(" [" + i + "] packageName=" + host.packageName + " uid=" + host.uid
180 + " hostId=" + host.hostId + " callbacks=" + host.callbacks
181 + " instances.size=" + host.instances.size() + " zombie=" + host.zombie);
182 }
183 }
184 }
185
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700186 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700188 synchronized (mAppWidgetIds) {
189 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190
191 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
192
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700193 AppWidgetId id = new AppWidgetId();
194 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 id.host = host;
196
197 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700198 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199
200 saveStateLocked();
201
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700202 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
204 }
205
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700206 public void deleteAppWidgetId(int appWidgetId) {
207 synchronized (mAppWidgetIds) {
208 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700210 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 saveStateLocked();
212 }
213 }
214 }
215
216 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700217 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 int callingUid = getCallingUid();
219 Host host = lookupHostLocked(callingUid, hostId);
220 if (host != null) {
221 deleteHostLocked(host);
222 saveStateLocked();
223 }
224 }
225 }
226
227 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700228 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 int callingUid = getCallingUid();
230 final int N = mHosts.size();
231 boolean changed = false;
232 for (int i=N-1; i>=0; i--) {
233 Host host = mHosts.get(i);
234 if (host.uid == callingUid) {
235 deleteHostLocked(host);
236 changed = true;
237 }
238 }
239 if (changed) {
240 saveStateLocked();
241 }
242 }
243 }
244
245 void deleteHostLocked(Host host) {
246 final int N = host.instances.size();
247 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700248 AppWidgetId id = host.instances.get(i);
249 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 }
251 host.instances.clear();
252 mHosts.remove(host);
253 // it's gone or going away, abruptly drop the callback connection
254 host.callbacks = null;
255 }
256
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700257 void deleteAppWidgetLocked(AppWidgetId id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 Host host = id.host;
259 host.instances.remove(id);
260 pruneHostLocked(host);
261
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700262 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263
264 Provider p = id.provider;
265 if (p != null) {
266 p.instances.remove(id);
267 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700268 // send the broacast saying that this appWidgetId has been deleted
269 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700271 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 mContext.sendBroadcast(intent);
273 if (p.instances.size() == 0) {
274 // cancel the future updates
275 cancelBroadcasts(p);
276
277 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700278 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 intent.setComponent(p.info.provider);
280 mContext.sendBroadcast(intent);
281 }
282 }
283 }
284 }
285
286 void cancelBroadcasts(Provider p) {
287 if (p.broadcast != null) {
288 mAlarmManager.cancel(p.broadcast);
289 long token = Binder.clearCallingIdentity();
290 try {
291 p.broadcast.cancel();
292 } finally {
293 Binder.restoreCallingIdentity(token);
294 }
295 p.broadcast = null;
296 }
297 }
298
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700299 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
300 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
301 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
302 synchronized (mAppWidgetIds) {
303 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700305 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 }
307 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700308 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 + id.provider.info.provider);
310 }
311 Provider p = lookupProviderLocked(provider);
312 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700313 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
315 if (p.zombie) {
316 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
317 + " safe mode: " + provider);
318 }
319
320 id.provider = p;
321 p.instances.add(id);
322 int instancesSize = p.instances.size();
323 if (instancesSize == 1) {
324 // tell the provider that it's ready
325 sendEnableIntentLocked(p);
326 }
327
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700328 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 // It's less critical when the next one happens, so when we schdule the next one,
330 // we add updatePeriodMillis to its start time. That time will have some slop,
331 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700332 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333
334 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700335 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 saveStateLocked();
337 }
338 }
339
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700340 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
341 synchronized (mAppWidgetIds) {
342 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 if (id != null && id.provider != null && !id.provider.zombie) {
344 return id.provider.info;
345 }
346 return null;
347 }
348 }
349
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700350 public RemoteViews getAppWidgetViews(int appWidgetId) {
351 synchronized (mAppWidgetIds) {
352 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 if (id != null) {
354 return id.views;
355 }
356 return null;
357 }
358 }
359
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700360 public List<AppWidgetProviderInfo> getInstalledProviders() {
361 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 final int N = mInstalledProviders.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700363 ArrayList<AppWidgetProviderInfo> result = new ArrayList(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 for (int i=0; i<N; i++) {
365 Provider p = mInstalledProviders.get(i);
366 if (!p.zombie) {
367 result.add(p.info);
368 }
369 }
370 return result;
371 }
372 }
373
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700374 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
375 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 return;
377 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700378 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 return;
380 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700381 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700383 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700385 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
386 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 }
388 }
389 }
390
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700391 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
392 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 Provider p = lookupProviderLocked(provider);
394 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700395 Log.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 return;
397 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700398 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 final int N = instances.size();
400 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700401 AppWidgetId id = instances.get(i);
402 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 }
404 }
405 }
406
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700407 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
408 // allow for stale appWidgetIds and other badness
409 // lookup also checks that the calling process can access the appWidgetId
410 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
412 id.views = views;
413
414 // is anyone listening?
415 if (id.host.callbacks != null) {
416 try {
417 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700418 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 } catch (RemoteException e) {
420 // It failed; remove the callback. No need to prune because
421 // we know that this host is still referenced by this instance.
422 id.host.callbacks = null;
423 }
424 }
425 }
426 }
427
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700428 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 List<RemoteViews> updatedViews) {
430 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700431 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
433 host.callbacks = callbacks;
434
435 updatedViews.clear();
436
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700437 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 int N = instances.size();
439 int[] updatedIds = new int[N];
440 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700441 AppWidgetId id = instances.get(i);
442 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 updatedViews.add(id.views);
444 }
445 return updatedIds;
446 }
447 }
448
449 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700450 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 Host host = lookupHostLocked(getCallingUid(), hostId);
452 host.callbacks = null;
453 pruneHostLocked(host);
454 }
455 }
456
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700457 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700459 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 return true;
461 }
462 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700463 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 return true;
465 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700466 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700468 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 return true;
470 }
471 // Nobody else can access it.
472 return false;
473 }
474
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700475 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700477 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700479 AppWidgetId id = mAppWidgetIds.get(i);
480 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 return id;
482 }
483 }
484 return null;
485 }
486
487 Provider lookupProviderLocked(ComponentName provider) {
488 final int N = mInstalledProviders.size();
489 for (int i=0; i<N; i++) {
490 Provider p = mInstalledProviders.get(i);
491 if (p.info.provider.equals(provider)) {
492 return p;
493 }
494 }
495 return null;
496 }
497
498 Host lookupHostLocked(int uid, int hostId) {
499 final int N = mHosts.size();
500 for (int i=0; i<N; i++) {
501 Host h = mHosts.get(i);
502 if (h.uid == uid && h.hostId == hostId) {
503 return h;
504 }
505 }
506 return null;
507 }
508
509 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
510 final int N = mHosts.size();
511 for (int i=0; i<N; i++) {
512 Host h = mHosts.get(i);
513 if (h.hostId == hostId && h.packageName.equals(packageName)) {
514 return h;
515 }
516 }
517 Host host = new Host();
518 host.packageName = packageName;
519 host.uid = uid;
520 host.hostId = hostId;
521 mHosts.add(host);
522 return host;
523 }
524
525 void pruneHostLocked(Host host) {
526 if (host.instances.size() == 0 && host.callbacks == null) {
527 mHosts.remove(host);
528 }
529 }
530
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700531 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 PackageManager pm = mPackageManager;
533
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700534 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
536 PackageManager.GET_META_DATA);
537
538 final int N = broadcastReceivers.size();
539 for (int i=0; i<N; i++) {
540 ResolveInfo ri = broadcastReceivers.get(i);
541 addProviderLocked(ri);
542 }
543 }
544
545 boolean addProviderLocked(ResolveInfo ri) {
546 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
547 ri.activityInfo.name), ri);
548 if (p != null) {
549 mInstalledProviders.add(p);
550 return true;
551 } else {
552 return false;
553 }
554 }
555
556 void removeProviderLocked(int index, Provider p) {
557 int N = p.instances.size();
558 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700559 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700561 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 // Stop telling the host about updates for this from now on
563 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700564 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700566 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 id.provider = null;
568 pruneHostLocked(id.host);
569 id.host = null;
570 }
571 p.instances.clear();
572 mInstalledProviders.remove(index);
573 // no need to send the DISABLE broadcast, since the receiver is gone anyway
574 cancelBroadcasts(p);
575 }
576
577 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700578 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 intent.setComponent(p.info.provider);
580 mContext.sendBroadcast(intent);
581 }
582
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700583 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
584 if (appWidgetIds != null && appWidgetIds.length > 0) {
585 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
586 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 intent.setComponent(p.info.provider);
588 mContext.sendBroadcast(intent);
589 }
590 }
591
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700592 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 if (p.info.updatePeriodMillis > 0) {
594 // if this is the first instance, set the alarm. otherwise,
595 // rely on the fact that we've already set it and that
596 // PendingIntent.getBroadcast will update the extras.
597 boolean alreadyRegistered = p.broadcast != null;
598 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700599 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
600 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 intent.setComponent(p.info.provider);
602 long token = Binder.clearCallingIdentity();
603 try {
604 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
605 PendingIntent.FLAG_UPDATE_CURRENT);
606 } finally {
607 Binder.restoreCallingIdentity(token);
608 }
609 if (!alreadyRegistered) {
610 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
611 SystemClock.elapsedRealtime() + p.info.updatePeriodMillis,
612 p.info.updatePeriodMillis, p.broadcast);
613 }
614 }
615 }
616
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700617 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700619 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700621 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700623 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
625
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700626 public int[] getAppWidgetIds(ComponentName provider) {
627 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 Provider p = lookupProviderLocked(provider);
629 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700630 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 } else {
632 return new int[0];
633 }
634 }
635 }
636
637 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
638 Provider p = null;
639
640 ActivityInfo activityInfo = ri.activityInfo;
641 XmlResourceParser parser = null;
642 try {
643 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700644 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 if (parser == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700646 Log.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
647 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648 return null;
649 }
650
651 AttributeSet attrs = Xml.asAttributeSet(parser);
652
653 int type;
654 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
655 && type != XmlPullParser.START_TAG) {
656 // drain whitespace, comments, etc.
657 }
658
659 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700660 if (!"appwidget-provider".equals(nodeName)) {
661 Log.w(TAG, "Meta-data does not start with appwidget-provider tag for"
662 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 return null;
664 }
665
666 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700667 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668
669 info.provider = component;
670 p.uid = activityInfo.applicationInfo.uid;
671
672 TypedArray sa = mContext.getResources().obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700673 com.android.internal.R.styleable.AppWidgetProviderInfo);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 info.minWidth = sa.getDimensionPixelSize(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700675 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 info.minHeight = sa.getDimensionPixelSize(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700677 com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700679 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700681 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700683 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 if (className != null) {
685 info.configure = new ComponentName(component.getPackageName(), className);
686 }
687 info.label = activityInfo.loadLabel(mPackageManager).toString();
688 info.icon = ri.getIconResource();
689 sa.recycle();
690 } catch (Exception e) {
691 // Ok to catch Exception here, because anything going wrong because
692 // of what a client process passes to us should not be fatal for the
693 // system process.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700694 Log.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 return null;
696 } finally {
697 if (parser != null) parser.close();
698 }
699 return p;
700 }
701
702 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
703 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
704 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
705 throw new PackageManager.NameNotFoundException();
706 }
707 return pkgInfo.applicationInfo.uid;
708 }
709
710 int enforceCallingUid(String packageName) throws IllegalArgumentException {
711 int callingUid = getCallingUid();
712 int packageUid;
713 try {
714 packageUid = getUidForPackage(packageName);
715 } catch (PackageManager.NameNotFoundException ex) {
716 throw new IllegalArgumentException("packageName and uid don't match packageName="
717 + packageName);
718 }
719 if (callingUid != packageUid) {
720 throw new IllegalArgumentException("packageName and uid don't match packageName="
721 + packageName);
722 }
723 return callingUid;
724 }
725
726 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700727 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 final int N = mInstalledProviders.size();
729 for (int i=0; i<N; i++) {
730 Provider p = mInstalledProviders.get(i);
731 if (p.instances.size() > 0) {
732 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700733 int[] appWidgetIds = getAppWidgetIds(p);
734 sendUpdateIntentLocked(p, appWidgetIds);
735 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 }
737 }
738 }
739 }
740
741 // only call from initialization -- it assumes that the data structures are all empty
742 void loadStateLocked() {
743 File temp = savedStateTempFile();
744 File real = savedStateRealFile();
745
746 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
747 // real one. if there is both a real file and a temp one, assume that the temp one isn't
748 // fully written and delete it.
749 if (real.exists()) {
750 readStateFromFileLocked(real);
751 if (temp.exists()) {
752 temp.delete();
753 }
754 } else if (temp.exists()) {
755 readStateFromFileLocked(temp);
756 temp.renameTo(real);
757 }
758 }
759
760 void saveStateLocked() {
761 File temp = savedStateTempFile();
762 File real = savedStateRealFile();
763
764 if (!real.exists()) {
765 // If the real one doesn't exist, it's either because this is the first time
766 // or because something went wrong while copying them. In this case, we can't
767 // trust anything that's in temp. In order to have the loadState code not
768 // use the temporary one until it's fully written, create an empty file
769 // for real, which will we'll shortly delete.
770 try {
771 real.createNewFile();
772 } catch (IOException e) {
773 }
774 }
775
776 if (temp.exists()) {
777 temp.delete();
778 }
779
780 writeStateToFileLocked(temp);
781
782 real.delete();
783 temp.renameTo(real);
784 }
785
786 void writeStateToFileLocked(File file) {
787 FileOutputStream stream = null;
788 int N;
789
790 try {
791 stream = new FileOutputStream(file, false);
792 XmlSerializer out = new FastXmlSerializer();
793 out.setOutput(stream, "utf-8");
794 out.startDocument(null, true);
795
796
797 out.startTag(null, "gs");
798
799 int providerIndex = 0;
800 N = mInstalledProviders.size();
801 for (int i=0; i<N; i++) {
802 Provider p = mInstalledProviders.get(i);
803 if (p.instances.size() > 0) {
804 out.startTag(null, "p");
805 out.attribute(null, "pkg", p.info.provider.getPackageName());
806 out.attribute(null, "cl", p.info.provider.getClassName());
807 out.endTag(null, "h");
808 p.tag = providerIndex;
809 providerIndex++;
810 }
811 }
812
813 N = mHosts.size();
814 for (int i=0; i<N; i++) {
815 Host host = mHosts.get(i);
816 out.startTag(null, "h");
817 out.attribute(null, "pkg", host.packageName);
818 out.attribute(null, "id", Integer.toHexString(host.hostId));
819 out.endTag(null, "h");
820 host.tag = i;
821 }
822
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700823 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700825 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700827 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 out.attribute(null, "h", Integer.toHexString(id.host.tag));
829 if (id.provider != null) {
830 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
831 }
832 out.endTag(null, "g");
833 }
834
835 out.endTag(null, "gs");
836
837 out.endDocument();
838 stream.close();
839 } catch (IOException e) {
840 try {
841 if (stream != null) {
842 stream.close();
843 }
844 } catch (IOException ex) {
845 }
846 if (file.exists()) {
847 file.delete();
848 }
849 }
850 }
851
852 void readStateFromFileLocked(File file) {
853 FileInputStream stream = null;
854
855 boolean success = false;
856
857 try {
858 stream = new FileInputStream(file);
859 XmlPullParser parser = Xml.newPullParser();
860 parser.setInput(stream, null);
861
862 int type;
863 int providerIndex = 0;
864 HashMap<Integer,Provider> loadedProviders = new HashMap();
865 do {
866 type = parser.next();
867 if (type == XmlPullParser.START_TAG) {
868 String tag = parser.getName();
869 if ("p".equals(tag)) {
870 // TODO: do we need to check that this package has the same signature
871 // as before?
872 String pkg = parser.getAttributeValue(null, "pkg");
873 String cl = parser.getAttributeValue(null, "cl");
874 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
875 if (p == null && mSafeMode) {
876 // if we're in safe mode, make a temporary one
877 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700878 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 p.info.provider = new ComponentName(pkg, cl);
880 p.zombie = true;
881 mInstalledProviders.add(p);
882 }
883 if (p != null) {
884 // if it wasn't uninstalled or something
885 loadedProviders.put(providerIndex, p);
886 }
887 providerIndex++;
888 }
889 else if ("h".equals(tag)) {
890 Host host = new Host();
891
892 // TODO: do we need to check that this package has the same signature
893 // as before?
894 host.packageName = parser.getAttributeValue(null, "pkg");
895 try {
896 host.uid = getUidForPackage(host.packageName);
897 } catch (PackageManager.NameNotFoundException ex) {
898 host.zombie = true;
899 }
900 if (!host.zombie || mSafeMode) {
901 // In safe mode, we don't discard the hosts we don't recognize
902 // so that they're not pruned from our list. Otherwise, we do.
903 host.hostId = Integer.parseInt(
904 parser.getAttributeValue(null, "id"), 16);
905 mHosts.add(host);
906 }
907 }
908 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700909 AppWidgetId id = new AppWidgetId();
910 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
911 if (id.appWidgetId >= mNextAppWidgetId) {
912 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 }
914
915 String providerString = parser.getAttributeValue(null, "p");
916 if (providerString != null) {
917 // there's no provider if it hasn't been bound yet.
918 // maybe we don't have to save this, but it brings the system
919 // to the state it was in.
920 int pIndex = Integer.parseInt(providerString, 16);
921 id.provider = loadedProviders.get(pIndex);
922 if (false) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700923 Log.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 + pIndex + " which is " + id.provider);
925 }
926 if (id.provider == null) {
927 // This provider is gone. We just let the host figure out
928 // that this happened when it fails to load it.
929 continue;
930 }
931 }
932
933 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
934 id.host = mHosts.get(hIndex);
935 if (id.host == null) {
936 // This host is gone.
937 continue;
938 }
939
940 if (id.provider != null) {
941 id.provider.instances.add(id);
942 }
943 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700944 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 }
946 }
947 } while (type != XmlPullParser.END_DOCUMENT);
948 success = true;
949 } catch (NullPointerException e) {
950 Log.w(TAG, "failed parsing " + file, e);
951 } catch (NumberFormatException e) {
952 Log.w(TAG, "failed parsing " + file, e);
953 } catch (XmlPullParserException e) {
954 Log.w(TAG, "failed parsing " + file, e);
955 } catch (IOException e) {
956 Log.w(TAG, "failed parsing " + file, e);
957 } catch (IndexOutOfBoundsException e) {
958 Log.w(TAG, "failed parsing " + file, e);
959 }
960 try {
961 if (stream != null) {
962 stream.close();
963 }
964 } catch (IOException e) {
965 }
966
967 if (success) {
968 // delete any hosts that didn't manage to get connected (should happen)
969 // if it matters, they'll be reconnected.
970 final int N = mHosts.size();
971 for (int i=0; i<N; i++) {
972 pruneHostLocked(mHosts.get(i));
973 }
974 } else {
975 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700976 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 mHosts.clear();
978 final int N = mInstalledProviders.size();
979 for (int i=0; i<N; i++) {
980 mInstalledProviders.get(i).instances.clear();
981 }
982 }
983 }
984
985 File savedStateTempFile() {
986 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
987 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
988 }
989
990 File savedStateRealFile() {
991 return new File("/data/system/" + SETTINGS_FILENAME);
992 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
993 }
994
995 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
996 public void onReceive(Context context, Intent intent) {
997 String action = intent.getAction();
998 //Log.d(TAG, "received " + action);
999 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1000 sendInitialBroadcasts();
1001 } else {
1002 Uri uri = intent.getData();
1003 if (uri == null) {
1004 return;
1005 }
1006 String pkgName = uri.getSchemeSpecificPart();
1007 if (pkgName == null) {
1008 return;
1009 }
1010
1011 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001012 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 Bundle extras = intent.getExtras();
1014 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1015 // The package was just upgraded
1016 updateProvidersForPackageLocked(pkgName);
1017 } else {
1018 // The package was just added
1019 addProvidersForPackageLocked(pkgName);
1020 }
1021 saveStateLocked();
1022 }
1023 }
1024 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
1025 Bundle extras = intent.getExtras();
1026 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1027 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1028 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001029 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 removeProvidersForPackageLocked(pkgName);
1031 saveStateLocked();
1032 }
1033 }
1034 }
1035 }
1036 }
1037 };
1038
1039 // TODO: If there's a better way of matching an intent filter against the
1040 // packages for a given package, use that.
1041 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001042 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1044 PackageManager.GET_META_DATA);
1045
1046 final int N = broadcastReceivers.size();
1047 for (int i=0; i<N; i++) {
1048 ResolveInfo ri = broadcastReceivers.get(i);
1049 ActivityInfo ai = ri.activityInfo;
1050
1051 if (pkgName.equals(ai.packageName)) {
1052 addProviderLocked(ri);
1053 }
1054 }
1055 }
1056
1057 // TODO: If there's a better way of matching an intent filter against the
1058 // packages for a given package, use that.
1059 void updateProvidersForPackageLocked(String pkgName) {
1060 HashSet<String> keep = new HashSet();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001061 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1063 PackageManager.GET_META_DATA);
1064
1065 // add the missing ones and collect which ones to keep
1066 int N = broadcastReceivers.size();
1067 for (int i=0; i<N; i++) {
1068 ResolveInfo ri = broadcastReceivers.get(i);
1069 ActivityInfo ai = ri.activityInfo;
1070 if (pkgName.equals(ai.packageName)) {
1071 ComponentName component = new ComponentName(ai.packageName, ai.name);
1072 Provider p = lookupProviderLocked(component);
1073 if (p == null) {
1074 if (addProviderLocked(ri)) {
1075 keep.add(ai.name);
1076 }
1077 } else {
1078 Provider parsed = parseProviderInfoXml(component, ri);
1079 if (parsed != null) {
1080 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001081 // Use the new AppWidgetProviderInfo.
1082 AppWidgetProviderInfo oldInfo = p.info;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 p.info = parsed.info;
1084 // If it's enabled
1085 final int M = p.instances.size();
1086 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001087 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 // Reschedule for the new updatePeriodMillis (don't worry about handling
1089 // it specially if updatePeriodMillis didn't change because we just sent
1090 // an update, and the next one will be updatePeriodMillis from now).
1091 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001092 registerForBroadcastsLocked(p, appWidgetIds);
1093 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001095 AppWidgetId id = p.instances.get(j);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 if (id.host != null && id.host.callbacks != null) {
1097 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001098 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 } catch (RemoteException ex) {
1100 // It failed; remove the callback. No need to prune because
1101 // we know that this host is still referenced by this
1102 // instance.
1103 id.host.callbacks = null;
1104 }
1105 }
1106 }
1107 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001108 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 }
1110 }
1111 }
1112 }
1113 }
1114
1115 // prune the ones we don't want to keep
1116 N = mInstalledProviders.size();
1117 for (int i=N-1; i>=0; i--) {
1118 Provider p = mInstalledProviders.get(i);
1119 if (pkgName.equals(p.info.provider.getPackageName())
1120 && !keep.contains(p.info.provider.getClassName())) {
1121 removeProviderLocked(i, p);
1122 }
1123 }
1124 }
1125
1126 void removeProvidersForPackageLocked(String pkgName) {
1127 int N = mInstalledProviders.size();
1128 for (int i=N-1; i>=0; i--) {
1129 Provider p = mInstalledProviders.get(i);
1130 if (pkgName.equals(p.info.provider.getPackageName())) {
1131 removeProviderLocked(i, p);
1132 }
1133 }
1134
1135 // Delete the hosts for this package too
1136 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001137 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 // so we don't need to worry about sending DISABLE broadcasts to them.
1139 N = mHosts.size();
1140 for (int i=N-1; i>=0; i--) {
1141 Host host = mHosts.get(i);
1142 if (pkgName.equals(host.packageName)) {
1143 deleteHostLocked(host);
1144 }
1145 }
1146 }
1147}
1148