blob: 815685655169756ffc231ca2e60c5d1c19943dff [file] [log] [blame]
Amith Yamasani742a6712011-05-04 14:49:28 -07001/*
2 * Copyright (C) 2011 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;
Amith Yamasani483f3b02012-03-13 16:08:00 -070020import android.app.AppGlobals;
Amith Yamasani742a6712011-05-04 14:49:28 -070021import android.app.PendingIntent;
22import android.appwidget.AppWidgetManager;
23import android.appwidget.AppWidgetProviderInfo;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
Amith Yamasani742a6712011-05-04 14:49:28 -070027import android.content.Intent.FilterComparison;
Michael Jurka61a5b012012-04-13 10:39:45 -070028import android.content.ServiceConnection;
Amith Yamasani742a6712011-05-04 14:49:28 -070029import android.content.pm.ActivityInfo;
30import android.content.pm.ApplicationInfo;
Amith Yamasani483f3b02012-03-13 16:08:00 -070031import android.content.pm.IPackageManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070032import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.ResolveInfo;
35import android.content.pm.ServiceInfo;
36import android.content.res.Resources;
37import android.content.res.TypedArray;
38import android.content.res.XmlResourceParser;
39import android.net.Uri;
40import android.os.Binder;
41import android.os.Bundle;
42import android.os.IBinder;
43import android.os.RemoteException;
44import android.os.SystemClock;
45import android.os.UserId;
46import android.util.AttributeSet;
47import android.util.Log;
48import android.util.Pair;
49import android.util.Slog;
50import android.util.TypedValue;
51import android.util.Xml;
Adam Cohen311c79c2012-05-10 14:44:38 -070052import android.view.WindowManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070053import android.widget.RemoteViews;
54
55import com.android.internal.appwidget.IAppWidgetHost;
56import com.android.internal.os.AtomicFile;
57import com.android.internal.util.FastXmlSerializer;
58import com.android.internal.widget.IRemoteViewsAdapterConnection;
59import com.android.internal.widget.IRemoteViewsFactory;
Amith Yamasani742a6712011-05-04 14:49:28 -070060
61import org.xmlpull.v1.XmlPullParser;
62import org.xmlpull.v1.XmlPullParserException;
63import org.xmlpull.v1.XmlSerializer;
64
65import java.io.File;
66import java.io.FileDescriptor;
67import java.io.FileInputStream;
68import java.io.FileNotFoundException;
69import java.io.FileOutputStream;
70import java.io.IOException;
71import java.io.PrintWriter;
72import java.util.ArrayList;
73import java.util.HashMap;
74import java.util.HashSet;
75import java.util.Iterator;
76import java.util.List;
77import java.util.Locale;
78import java.util.Set;
79
80class AppWidgetServiceImpl {
81
82 private static final String TAG = "AppWidgetServiceImpl";
83 private static final String SETTINGS_FILENAME = "appwidgets.xml";
84 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
85
86 /*
87 * When identifying a Host or Provider based on the calling process, use the uid field. When
88 * identifying a Host or Provider based on a package manager broadcast, use the package given.
89 */
90
91 static class Provider {
92 int uid;
93 AppWidgetProviderInfo info;
94 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
95 PendingIntent broadcast;
96 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
97
98 int tag; // for use while saving state (the index)
99 }
100
101 static class Host {
102 int uid;
103 int hostId;
104 String packageName;
105 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
106 IAppWidgetHost callbacks;
107 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
108
109 int tag; // for use while saving state (the index)
110 }
111
112 static class AppWidgetId {
113 int appWidgetId;
114 Provider provider;
115 RemoteViews views;
Adam Cohend2097eb2012-05-01 18:10:28 -0700116 Bundle options;
Amith Yamasani742a6712011-05-04 14:49:28 -0700117 Host host;
118 }
119
120 /**
121 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
122 * needs to be a static inner class since a reference to the ServiceConnection is held globally
123 * and may lead us to leak AppWidgetService instances (if there were more than one).
124 */
125 static class ServiceConnectionProxy implements ServiceConnection {
126 private final IBinder mConnectionCb;
127
128 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
129 mConnectionCb = connectionCb;
130 }
131
132 public void onServiceConnected(ComponentName name, IBinder service) {
133 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
134 .asInterface(mConnectionCb);
135 try {
136 cb.onServiceConnected(service);
137 } catch (Exception e) {
138 e.printStackTrace();
139 }
140 }
141
142 public void onServiceDisconnected(ComponentName name) {
143 disconnect();
144 }
145
146 public void disconnect() {
147 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
148 .asInterface(mConnectionCb);
149 try {
150 cb.onServiceDisconnected();
151 } catch (Exception e) {
152 e.printStackTrace();
153 }
154 }
155 }
156
157 // Manages active connections to RemoteViewsServices
158 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
159 // Manages persistent references to RemoteViewsServices from different App Widgets
160 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
161
162 Context mContext;
163 Locale mLocale;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700164 IPackageManager mPm;
Amith Yamasani742a6712011-05-04 14:49:28 -0700165 AlarmManager mAlarmManager;
166 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
167 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
168 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
169 ArrayList<Host> mHosts = new ArrayList<Host>();
Michael Jurka61a5b012012-04-13 10:39:45 -0700170 // set of package names
171 HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
Amith Yamasani742a6712011-05-04 14:49:28 -0700172 boolean mSafeMode;
173 int mUserId;
174 boolean mStateLoaded;
Adam Cohen311c79c2012-05-10 14:44:38 -0700175 int mMaxWidgetBitmapMemory;
Amith Yamasani742a6712011-05-04 14:49:28 -0700176
177 // These are for debugging only -- widgets are going missing in some rare instances
178 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
179 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
180
181 AppWidgetServiceImpl(Context context, int userId) {
182 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700183 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700184 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
185 mUserId = userId;
Adam Cohen311c79c2012-05-10 14:44:38 -0700186 computeMaximumWidgetBitmapMemory();
187 }
188
189 void computeMaximumWidgetBitmapMemory() {
190 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
191 int height = wm.getDefaultDisplay().getRawHeight();
192 int width = wm.getDefaultDisplay().getRawWidth();
193 mMaxWidgetBitmapMemory = 4 * width * height;
Amith Yamasani742a6712011-05-04 14:49:28 -0700194 }
195
196 public void systemReady(boolean safeMode) {
197 mSafeMode = safeMode;
198
199 synchronized (mAppWidgetIds) {
200 ensureStateLoadedLocked();
201 }
202 }
203
204 void onConfigurationChanged() {
205 Locale revised = Locale.getDefault();
206 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
207 mLocale = revised;
208
209 synchronized (mAppWidgetIds) {
210 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700211 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
212 // list of installed providers and skip providers that we don't need to update.
213 // Also note that remove the provider does not clear the Provider component data.
214 ArrayList<Provider> installedProviders =
215 new ArrayList<Provider>(mInstalledProviders);
216 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
217 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700218 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700219 Provider p = installedProviders.get(i);
220 ComponentName cn = p.info.provider;
221 if (!removedProviders.contains(cn)) {
222 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
223 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700224 }
225 saveStateLocked();
226 }
227 }
228 }
229
230 void onBroadcastReceived(Intent intent) {
231 final String action = intent.getAction();
232 boolean added = false;
233 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700234 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700235 String pkgList[] = null;
236 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
237 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
238 added = true;
239 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
240 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
241 added = false;
242 } else {
243 Uri uri = intent.getData();
244 if (uri == null) {
245 return;
246 }
247 String pkgName = uri.getSchemeSpecificPart();
248 if (pkgName == null) {
249 return;
250 }
251 pkgList = new String[] { pkgName };
252 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
253 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
254 }
255 if (pkgList == null || pkgList.length == 0) {
256 return;
257 }
258 if (added || changed) {
259 synchronized (mAppWidgetIds) {
260 ensureStateLoadedLocked();
261 Bundle extras = intent.getExtras();
262 if (changed
263 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
264 for (String pkgName : pkgList) {
265 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700266 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700267 }
268 } else {
269 // The package was just added
270 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700271 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700272 }
273 }
274 saveStateLocked();
275 }
276 } else {
277 Bundle extras = intent.getExtras();
278 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
279 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
280 } else {
281 synchronized (mAppWidgetIds) {
282 ensureStateLoadedLocked();
283 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700284 providersModified |= removeProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700285 saveStateLocked();
286 }
287 }
288 }
289 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700290
291 if (providersModified) {
292 // If the set of providers has been modified, notify each active AppWidgetHost
293 synchronized (mAppWidgetIds) {
294 ensureStateLoadedLocked();
295 notifyHostsForProvidersChangedLocked();
296 }
297 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700298 }
299
300 private void dumpProvider(Provider p, int index, PrintWriter pw) {
301 AppWidgetProviderInfo info = p.info;
302 pw.print(" ["); pw.print(index); pw.print("] provider ");
303 pw.print(info.provider.flattenToShortString());
304 pw.println(':');
305 pw.print(" min=("); pw.print(info.minWidth);
306 pw.print("x"); pw.print(info.minHeight);
307 pw.print(") minResize=("); pw.print(info.minResizeWidth);
308 pw.print("x"); pw.print(info.minResizeHeight);
309 pw.print(") updatePeriodMillis=");
310 pw.print(info.updatePeriodMillis);
311 pw.print(" resizeMode=");
312 pw.print(info.resizeMode);
313 pw.print(" autoAdvanceViewId=");
314 pw.print(info.autoAdvanceViewId);
315 pw.print(" initialLayout=#");
316 pw.print(Integer.toHexString(info.initialLayout));
317 pw.print(" zombie="); pw.println(p.zombie);
318 }
319
320 private void dumpHost(Host host, int index, PrintWriter pw) {
321 pw.print(" ["); pw.print(index); pw.print("] hostId=");
322 pw.print(host.hostId); pw.print(' ');
323 pw.print(host.packageName); pw.print('/');
324 pw.print(host.uid); pw.println(':');
325 pw.print(" callbacks="); pw.println(host.callbacks);
326 pw.print(" instances.size="); pw.print(host.instances.size());
327 pw.print(" zombie="); pw.println(host.zombie);
328 }
329
330 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
331 pw.print(" ["); pw.print(index); pw.print("] id=");
332 pw.println(id.appWidgetId);
333 pw.print(" hostId=");
334 pw.print(id.host.hostId); pw.print(' ');
335 pw.print(id.host.packageName); pw.print('/');
336 pw.println(id.host.uid);
337 if (id.provider != null) {
338 pw.print(" provider=");
339 pw.println(id.provider.info.provider.flattenToShortString());
340 }
341 if (id.host != null) {
342 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
343 }
344 if (id.views != null) {
345 pw.print(" views="); pw.println(id.views);
346 }
347 }
348
349 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
350 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
351 != PackageManager.PERMISSION_GRANTED) {
352 pw.println("Permission Denial: can't dump from from pid="
353 + Binder.getCallingPid()
354 + ", uid=" + Binder.getCallingUid());
355 return;
356 }
357
358 synchronized (mAppWidgetIds) {
359 int N = mInstalledProviders.size();
360 pw.println("Providers:");
361 for (int i=0; i<N; i++) {
362 dumpProvider(mInstalledProviders.get(i), i, pw);
363 }
364
365 N = mAppWidgetIds.size();
366 pw.println(" ");
367 pw.println("AppWidgetIds:");
368 for (int i=0; i<N; i++) {
369 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
370 }
371
372 N = mHosts.size();
373 pw.println(" ");
374 pw.println("Hosts:");
375 for (int i=0; i<N; i++) {
376 dumpHost(mHosts.get(i), i, pw);
377 }
378
379 N = mDeletedProviders.size();
380 pw.println(" ");
381 pw.println("Deleted Providers:");
382 for (int i=0; i<N; i++) {
383 dumpProvider(mDeletedProviders.get(i), i, pw);
384 }
385
386 N = mDeletedHosts.size();
387 pw.println(" ");
388 pw.println("Deleted Hosts:");
389 for (int i=0; i<N; i++) {
390 dumpHost(mDeletedHosts.get(i), i, pw);
391 }
392 }
393 }
394
395 private void ensureStateLoadedLocked() {
396 if (!mStateLoaded) {
397 loadAppWidgetList();
398 loadStateLocked();
399 mStateLoaded = true;
400 }
401 }
402
403 public int allocateAppWidgetId(String packageName, int hostId) {
404 int callingUid = enforceCallingUid(packageName);
405 synchronized (mAppWidgetIds) {
406 ensureStateLoadedLocked();
407 int appWidgetId = mNextAppWidgetId++;
408
409 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
410
411 AppWidgetId id = new AppWidgetId();
412 id.appWidgetId = appWidgetId;
413 id.host = host;
414
415 host.instances.add(id);
416 mAppWidgetIds.add(id);
417
418 saveStateLocked();
419
420 return appWidgetId;
421 }
422 }
423
424 public void deleteAppWidgetId(int appWidgetId) {
425 synchronized (mAppWidgetIds) {
426 ensureStateLoadedLocked();
427 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
428 if (id != null) {
429 deleteAppWidgetLocked(id);
430 saveStateLocked();
431 }
432 }
433 }
434
435 public void deleteHost(int hostId) {
436 synchronized (mAppWidgetIds) {
437 ensureStateLoadedLocked();
438 int callingUid = Binder.getCallingUid();
439 Host host = lookupHostLocked(callingUid, hostId);
440 if (host != null) {
441 deleteHostLocked(host);
442 saveStateLocked();
443 }
444 }
445 }
446
447 public void deleteAllHosts() {
448 synchronized (mAppWidgetIds) {
449 ensureStateLoadedLocked();
450 int callingUid = Binder.getCallingUid();
451 final int N = mHosts.size();
452 boolean changed = false;
453 for (int i = N - 1; i >= 0; i--) {
454 Host host = mHosts.get(i);
455 if (host.uid == callingUid) {
456 deleteHostLocked(host);
457 changed = true;
458 }
459 }
460 if (changed) {
461 saveStateLocked();
462 }
463 }
464 }
465
466 void deleteHostLocked(Host host) {
467 final int N = host.instances.size();
468 for (int i = N - 1; i >= 0; i--) {
469 AppWidgetId id = host.instances.get(i);
470 deleteAppWidgetLocked(id);
471 }
472 host.instances.clear();
473 mHosts.remove(host);
474 mDeletedHosts.add(host);
475 // it's gone or going away, abruptly drop the callback connection
476 host.callbacks = null;
477 }
478
479 void deleteAppWidgetLocked(AppWidgetId id) {
480 // We first unbind all services that are bound to this id
481 unbindAppWidgetRemoteViewsServicesLocked(id);
482
483 Host host = id.host;
484 host.instances.remove(id);
485 pruneHostLocked(host);
486
487 mAppWidgetIds.remove(id);
488
489 Provider p = id.provider;
490 if (p != null) {
491 p.instances.remove(id);
492 if (!p.zombie) {
493 // send the broacast saying that this appWidgetId has been deleted
494 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
495 intent.setComponent(p.info.provider);
496 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Amith Yamasani67cf7d32012-02-16 14:31:23 -0800497 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700498 if (p.instances.size() == 0) {
499 // cancel the future updates
500 cancelBroadcasts(p);
501
502 // send the broacast saying that the provider is not in use any more
503 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
504 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -0800505 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700506 }
507 }
508 }
509 }
510
511 void cancelBroadcasts(Provider p) {
512 if (p.broadcast != null) {
513 mAlarmManager.cancel(p.broadcast);
514 long token = Binder.clearCallingIdentity();
515 try {
516 p.broadcast.cancel();
517 } finally {
518 Binder.restoreCallingIdentity(token);
519 }
520 p.broadcast = null;
521 }
522 }
523
Michael Jurka61a5b012012-04-13 10:39:45 -0700524 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700525 final long ident = Binder.clearCallingIdentity();
526 try {
527 synchronized (mAppWidgetIds) {
528 ensureStateLoadedLocked();
529 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
530 if (id == null) {
531 throw new IllegalArgumentException("bad appWidgetId");
532 }
533 if (id.provider != null) {
534 throw new IllegalArgumentException("appWidgetId " + appWidgetId
535 + " already bound to " + id.provider.info.provider);
536 }
537 Provider p = lookupProviderLocked(provider);
538 if (p == null) {
539 throw new IllegalArgumentException("not a appwidget provider: " + provider);
540 }
541 if (p.zombie) {
542 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
543 + " safe mode: " + provider);
544 }
545
Amith Yamasani742a6712011-05-04 14:49:28 -0700546 id.provider = p;
547 p.instances.add(id);
548 int instancesSize = p.instances.size();
549 if (instancesSize == 1) {
550 // tell the provider that it's ready
551 sendEnableIntentLocked(p);
552 }
553
554 // send an update now -- We need this update now, and just for this appWidgetId.
555 // It's less critical when the next one happens, so when we schedule the next one,
556 // we add updatePeriodMillis to its start time. That time will have some slop,
557 // but that's okay.
558 sendUpdateIntentLocked(p, new int[] { appWidgetId });
559
560 // schedule the future updates
561 registerForBroadcastsLocked(p, getAppWidgetIds(p));
562 saveStateLocked();
563 }
564 } finally {
565 Binder.restoreCallingIdentity(ident);
566 }
567 }
568
Michael Jurka61a5b012012-04-13 10:39:45 -0700569 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
570 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
571 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
572 bindAppWidgetIdImpl(appWidgetId, provider);
573 }
574
575 public boolean bindAppWidgetIdIfAllowed(
576 String packageName, int appWidgetId, ComponentName provider) {
577 try {
578 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
579 } catch (SecurityException se) {
580 if (!callerHasBindAppWidgetPermission(packageName)) {
581 return false;
582 }
583 }
584 bindAppWidgetIdImpl(appWidgetId, provider);
585 return true;
586 }
587
588 private boolean callerHasBindAppWidgetPermission(String packageName) {
589 int callingUid = Binder.getCallingUid();
590 try {
591 if (!UserId.isSameApp(callingUid, getUidForPackage(packageName))) {
592 return false;
593 }
594 } catch (Exception e) {
595 return false;
596 }
597 synchronized (mAppWidgetIds) {
598 ensureStateLoadedLocked();
599 return mPackagesWithBindWidgetPermission.contains(packageName);
600 }
601 }
602
603 public boolean hasBindAppWidgetPermission(String packageName) {
604 mContext.enforceCallingPermission(
605 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
606 "hasBindAppWidgetPermission packageName=" + packageName);
607
608 synchronized (mAppWidgetIds) {
609 ensureStateLoadedLocked();
610 return mPackagesWithBindWidgetPermission.contains(packageName);
611 }
612 }
613
614 public void setBindAppWidgetPermission(String packageName, boolean permission) {
615 mContext.enforceCallingPermission(
616 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
617 "setBindAppWidgetPermission packageName=" + packageName);
618
619 synchronized (mAppWidgetIds) {
620 ensureStateLoadedLocked();
621 if (permission) {
622 mPackagesWithBindWidgetPermission.add(packageName);
623 } else {
624 mPackagesWithBindWidgetPermission.remove(packageName);
625 }
626 }
627 saveStateLocked();
628 }
629
Amith Yamasani742a6712011-05-04 14:49:28 -0700630 // Binds to a specific RemoteViewsService
631 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
632 synchronized (mAppWidgetIds) {
633 ensureStateLoadedLocked();
634 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
635 if (id == null) {
636 throw new IllegalArgumentException("bad appWidgetId");
637 }
638 final ComponentName componentName = intent.getComponent();
639 try {
640 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
641 PackageManager.GET_PERMISSIONS);
642 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
643 throw new SecurityException("Selected service does not require "
644 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
645 }
646 } catch (PackageManager.NameNotFoundException e) {
647 throw new IllegalArgumentException("Unknown component " + componentName);
648 }
649
650 // If there is already a connection made for this service intent, then disconnect from
651 // that first. (This does not allow multiple connections to the same service under
652 // the same key)
653 ServiceConnectionProxy conn = null;
654 FilterComparison fc = new FilterComparison(intent);
655 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
656 if (mBoundRemoteViewsServices.containsKey(key)) {
657 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
658 conn.disconnect();
659 mContext.unbindService(conn);
660 mBoundRemoteViewsServices.remove(key);
661 }
662
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800663 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700664 // Bind to the RemoteViewsService (which will trigger a callback to the
665 // RemoteViewsAdapter.onServiceConnected())
666 final long token = Binder.clearCallingIdentity();
667 try {
668 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800669 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700670 mBoundRemoteViewsServices.put(key, conn);
671 } finally {
672 Binder.restoreCallingIdentity(token);
673 }
674
675 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
676 // when we can call back to the RemoteViewsService later to destroy associated
677 // factories.
678 incrementAppWidgetServiceRefCount(appWidgetId, fc);
679 }
680 }
681
682 // Unbinds from a specific RemoteViewsService
683 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
684 synchronized (mAppWidgetIds) {
685 ensureStateLoadedLocked();
686 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
687 // RemoteViewsAdapter)
688 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
689 intent));
690 if (mBoundRemoteViewsServices.containsKey(key)) {
691 // We don't need to use the appWidgetId until after we are sure there is something
692 // to unbind. Note that this may mask certain issues with apps calling unbind()
693 // more than necessary.
694 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
695 if (id == null) {
696 throw new IllegalArgumentException("bad appWidgetId");
697 }
698
699 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
700 .get(key);
701 conn.disconnect();
702 mContext.unbindService(conn);
703 mBoundRemoteViewsServices.remove(key);
704 } else {
705 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
706 }
707 }
708 }
709
710 // Unbinds from a RemoteViewsService when we delete an app widget
711 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
712 int appWidgetId = id.appWidgetId;
713 // Unbind all connections to Services bound to this AppWidgetId
714 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
715 .iterator();
716 while (it.hasNext()) {
717 final Pair<Integer, Intent.FilterComparison> key = it.next();
718 if (key.first.intValue() == appWidgetId) {
719 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
720 .get(key);
721 conn.disconnect();
722 mContext.unbindService(conn);
723 it.remove();
724 }
725 }
726
727 // Check if we need to destroy any services (if no other app widgets are
728 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800729 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700730 }
731
732 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800733 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700734 final ServiceConnection conn = new ServiceConnection() {
735 @Override
736 public void onServiceConnected(ComponentName name, IBinder service) {
737 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
738 try {
739 cb.onDestroy(intent);
740 } catch (RemoteException e) {
741 e.printStackTrace();
742 } catch (RuntimeException e) {
743 e.printStackTrace();
744 }
745 mContext.unbindService(this);
746 }
747
748 @Override
749 public void onServiceDisconnected(android.content.ComponentName name) {
750 // Do nothing
751 }
752 };
753
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800754 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700755 // Bind to the service and remove the static intent->factory mapping in the
756 // RemoteViewsService.
757 final long token = Binder.clearCallingIdentity();
758 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800759 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700760 } finally {
761 Binder.restoreCallingIdentity(token);
762 }
763 }
764
765 // Adds to the ref-count for a given RemoteViewsService intent
766 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
767 HashSet<Integer> appWidgetIds = null;
768 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
769 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
770 } else {
771 appWidgetIds = new HashSet<Integer>();
772 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
773 }
774 appWidgetIds.add(appWidgetId);
775 }
776
777 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
778 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800779 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700780 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
781 while (it.hasNext()) {
782 final FilterComparison key = it.next();
783 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800784 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700785 // If we have removed the last app widget referencing this service, then we
786 // should destroy it and remove it from this set
787 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800788 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700789 it.remove();
790 }
791 }
792 }
793 }
794
795 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
796 synchronized (mAppWidgetIds) {
797 ensureStateLoadedLocked();
798 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
799 if (id != null && id.provider != null && !id.provider.zombie) {
800 return id.provider.info;
801 }
802 return null;
803 }
804 }
805
806 public RemoteViews getAppWidgetViews(int appWidgetId) {
807 synchronized (mAppWidgetIds) {
808 ensureStateLoadedLocked();
809 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
810 if (id != null) {
811 return id.views;
812 }
813 return null;
814 }
815 }
816
817 public List<AppWidgetProviderInfo> getInstalledProviders() {
818 synchronized (mAppWidgetIds) {
819 ensureStateLoadedLocked();
820 final int N = mInstalledProviders.size();
821 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
822 for (int i = 0; i < N; i++) {
823 Provider p = mInstalledProviders.get(i);
824 if (!p.zombie) {
825 result.add(p.info);
826 }
827 }
828 return result;
829 }
830 }
831
832 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
833 if (appWidgetIds == null) {
834 return;
835 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700836
837 int bitmapMemoryUsage = views.estimateMemoryUsage();
838 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
839 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
840 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
841 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
842 " fill the device's screen once.");
843 }
844
Amith Yamasani742a6712011-05-04 14:49:28 -0700845 if (appWidgetIds.length == 0) {
846 return;
847 }
848 final int N = appWidgetIds.length;
849
850 synchronized (mAppWidgetIds) {
851 ensureStateLoadedLocked();
852 for (int i = 0; i < N; i++) {
853 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
854 updateAppWidgetInstanceLocked(id, views);
855 }
856 }
857 }
858
Adam Cohend2097eb2012-05-01 18:10:28 -0700859 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700860 synchronized (mAppWidgetIds) {
861 ensureStateLoadedLocked();
862 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
863
864 if (id == null) {
865 return;
866 }
867 Provider p = id.provider;
Adam Cohend2097eb2012-05-01 18:10:28 -0700868 id.options = options;
Adam Cohene8724c82012-04-19 17:11:40 -0700869
870 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700871 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700872 intent.setComponent(p.info.provider);
873 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700874 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
Adam Cohene8724c82012-04-19 17:11:40 -0700875 mContext.sendBroadcast(intent, mUserId);
876 }
877 }
878
Adam Cohend2097eb2012-05-01 18:10:28 -0700879 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -0700880 synchronized (mAppWidgetIds) {
881 ensureStateLoadedLocked();
882 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700883 if (id != null && id.options != null) {
884 return id.options;
Adam Cohene8724c82012-04-19 17:11:40 -0700885 } else {
886 return Bundle.EMPTY;
887 }
888 }
889 }
890
Amith Yamasani742a6712011-05-04 14:49:28 -0700891 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
892 if (appWidgetIds == null) {
893 return;
894 }
895 if (appWidgetIds.length == 0) {
896 return;
897 }
898 final int N = appWidgetIds.length;
899
900 synchronized (mAppWidgetIds) {
901 ensureStateLoadedLocked();
902 for (int i = 0; i < N; i++) {
903 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
904 updateAppWidgetInstanceLocked(id, views, true);
905 }
906 }
907 }
908
909 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
910 if (appWidgetIds == null) {
911 return;
912 }
913 if (appWidgetIds.length == 0) {
914 return;
915 }
916 final int N = appWidgetIds.length;
917
918 synchronized (mAppWidgetIds) {
919 ensureStateLoadedLocked();
920 for (int i = 0; i < N; i++) {
921 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
922 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
923 }
924 }
925 }
926
927 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
928 synchronized (mAppWidgetIds) {
929 ensureStateLoadedLocked();
930 Provider p = lookupProviderLocked(provider);
931 if (p == null) {
932 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
933 return;
934 }
935 ArrayList<AppWidgetId> instances = p.instances;
936 final int callingUid = Binder.getCallingUid();
937 final int N = instances.size();
938 for (int i = 0; i < N; i++) {
939 AppWidgetId id = instances.get(i);
940 if (canAccessAppWidgetId(id, callingUid)) {
941 updateAppWidgetInstanceLocked(id, views);
942 }
943 }
944 }
945 }
946
947 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
948 updateAppWidgetInstanceLocked(id, views, false);
949 }
950
951 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
952 // allow for stale appWidgetIds and other badness
953 // lookup also checks that the calling process can access the appWidgetId
954 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
955 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
956
957 // We do not want to save this RemoteViews
958 if (!isPartialUpdate)
959 id.views = views;
960
961 // is anyone listening?
962 if (id.host.callbacks != null) {
963 try {
964 // the lock is held, but this is a oneway call
965 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
966 } catch (RemoteException e) {
967 // It failed; remove the callback. No need to prune because
968 // we know that this host is still referenced by this instance.
969 id.host.callbacks = null;
970 }
971 }
972 }
973 }
974
975 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
976 // allow for stale appWidgetIds and other badness
977 // lookup also checks that the calling process can access the appWidgetId
978 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
979 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
980 // is anyone listening?
981 if (id.host.callbacks != null) {
982 try {
983 // the lock is held, but this is a oneway call
984 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
985 } catch (RemoteException e) {
986 // It failed; remove the callback. No need to prune because
987 // we know that this host is still referenced by this instance.
988 id.host.callbacks = null;
989 }
990 }
991
992 // If the host is unavailable, then we call the associated
993 // RemoteViewsFactory.onDataSetChanged() directly
994 if (id.host.callbacks == null) {
995 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
996 for (FilterComparison key : keys) {
997 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
998 Intent intent = key.getIntent();
999
1000 final ServiceConnection conn = new ServiceConnection() {
1001 @Override
1002 public void onServiceConnected(ComponentName name, IBinder service) {
1003 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1004 .asInterface(service);
1005 try {
1006 cb.onDataSetChangedAsync();
1007 } catch (RemoteException e) {
1008 e.printStackTrace();
1009 } catch (RuntimeException e) {
1010 e.printStackTrace();
1011 }
1012 mContext.unbindService(this);
1013 }
1014
1015 @Override
1016 public void onServiceDisconnected(android.content.ComponentName name) {
1017 // Do nothing
1018 }
1019 };
1020
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001021 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001022 // Bind to the service and call onDataSetChanged()
1023 final long token = Binder.clearCallingIdentity();
1024 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001025 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001026 } finally {
1027 Binder.restoreCallingIdentity(token);
1028 }
1029 }
1030 }
1031 }
1032 }
1033 }
1034
1035 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1036 List<RemoteViews> updatedViews) {
1037 int callingUid = enforceCallingUid(packageName);
1038 synchronized (mAppWidgetIds) {
1039 ensureStateLoadedLocked();
1040 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1041 host.callbacks = callbacks;
1042
1043 updatedViews.clear();
1044
1045 ArrayList<AppWidgetId> instances = host.instances;
1046 int N = instances.size();
1047 int[] updatedIds = new int[N];
1048 for (int i = 0; i < N; i++) {
1049 AppWidgetId id = instances.get(i);
1050 updatedIds[i] = id.appWidgetId;
1051 updatedViews.add(id.views);
1052 }
1053 return updatedIds;
1054 }
1055 }
1056
1057 public void stopListening(int hostId) {
1058 synchronized (mAppWidgetIds) {
1059 ensureStateLoadedLocked();
1060 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1061 if (host != null) {
1062 host.callbacks = null;
1063 pruneHostLocked(host);
1064 }
1065 }
1066 }
1067
1068 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1069 if (id.host.uid == callingUid) {
1070 // Apps hosting the AppWidget have access to it.
1071 return true;
1072 }
1073 if (id.provider != null && id.provider.uid == callingUid) {
1074 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1075 return true;
1076 }
1077 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1078 // Apps that can bind have access to all appWidgetIds.
1079 return true;
1080 }
1081 // Nobody else can access it.
1082 return false;
1083 }
1084
1085 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1086 int callingUid = Binder.getCallingUid();
1087 final int N = mAppWidgetIds.size();
1088 for (int i = 0; i < N; i++) {
1089 AppWidgetId id = mAppWidgetIds.get(i);
1090 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1091 return id;
1092 }
1093 }
1094 return null;
1095 }
1096
1097 Provider lookupProviderLocked(ComponentName provider) {
1098 final int N = mInstalledProviders.size();
1099 for (int i = 0; i < N; i++) {
1100 Provider p = mInstalledProviders.get(i);
1101 if (p.info.provider.equals(provider)) {
1102 return p;
1103 }
1104 }
1105 return null;
1106 }
1107
1108 Host lookupHostLocked(int uid, int hostId) {
1109 final int N = mHosts.size();
1110 for (int i = 0; i < N; i++) {
1111 Host h = mHosts.get(i);
1112 if (h.uid == uid && h.hostId == hostId) {
1113 return h;
1114 }
1115 }
1116 return null;
1117 }
1118
1119 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1120 final int N = mHosts.size();
1121 for (int i = 0; i < N; i++) {
1122 Host h = mHosts.get(i);
1123 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1124 return h;
1125 }
1126 }
1127 Host host = new Host();
1128 host.packageName = packageName;
1129 host.uid = uid;
1130 host.hostId = hostId;
1131 mHosts.add(host);
1132 return host;
1133 }
1134
1135 void pruneHostLocked(Host host) {
1136 if (host.instances.size() == 0 && host.callbacks == null) {
1137 mHosts.remove(host);
1138 }
1139 }
1140
1141 void loadAppWidgetList() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001142 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001143 try {
1144 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1145 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1146 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001147
Amith Yamasani483f3b02012-03-13 16:08:00 -07001148 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1149 for (int i = 0; i < N; i++) {
1150 ResolveInfo ri = broadcastReceivers.get(i);
1151 addProviderLocked(ri);
1152 }
1153 } catch (RemoteException re) {
1154 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001155 }
1156 }
1157
1158 boolean addProviderLocked(ResolveInfo ri) {
1159 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1160 return false;
1161 }
1162 if (!ri.activityInfo.isEnabled()) {
1163 return false;
1164 }
1165 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1166 ri.activityInfo.name), ri);
1167 if (p != null) {
1168 mInstalledProviders.add(p);
1169 return true;
1170 } else {
1171 return false;
1172 }
1173 }
1174
1175 void removeProviderLocked(int index, Provider p) {
1176 int N = p.instances.size();
1177 for (int i = 0; i < N; i++) {
1178 AppWidgetId id = p.instances.get(i);
1179 // Call back with empty RemoteViews
1180 updateAppWidgetInstanceLocked(id, null);
1181 // Stop telling the host about updates for this from now on
1182 cancelBroadcasts(p);
1183 // clear out references to this appWidgetId
1184 id.host.instances.remove(id);
1185 mAppWidgetIds.remove(id);
1186 id.provider = null;
1187 pruneHostLocked(id.host);
1188 id.host = null;
1189 }
1190 p.instances.clear();
1191 mInstalledProviders.remove(index);
1192 mDeletedProviders.add(p);
1193 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1194 cancelBroadcasts(p);
1195 }
1196
1197 void sendEnableIntentLocked(Provider p) {
1198 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1199 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001200 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001201 }
1202
1203 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1204 if (appWidgetIds != null && appWidgetIds.length > 0) {
1205 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1206 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1207 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001208 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001209 }
1210 }
1211
1212 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1213 if (p.info.updatePeriodMillis > 0) {
1214 // if this is the first instance, set the alarm. otherwise,
1215 // rely on the fact that we've already set it and that
1216 // PendingIntent.getBroadcast will update the extras.
1217 boolean alreadyRegistered = p.broadcast != null;
1218 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1219 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1220 intent.setComponent(p.info.provider);
1221 long token = Binder.clearCallingIdentity();
1222 try {
1223 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1224 PendingIntent.FLAG_UPDATE_CURRENT);
1225 } finally {
1226 Binder.restoreCallingIdentity(token);
1227 }
1228 if (!alreadyRegistered) {
1229 long period = p.info.updatePeriodMillis;
1230 if (period < MIN_UPDATE_PERIOD) {
1231 period = MIN_UPDATE_PERIOD;
1232 }
1233 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1234 .elapsedRealtime()
1235 + period, period, p.broadcast);
1236 }
1237 }
1238 }
1239
1240 static int[] getAppWidgetIds(Provider p) {
1241 int instancesSize = p.instances.size();
1242 int appWidgetIds[] = new int[instancesSize];
1243 for (int i = 0; i < instancesSize; i++) {
1244 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1245 }
1246 return appWidgetIds;
1247 }
1248
1249 public int[] getAppWidgetIds(ComponentName provider) {
1250 synchronized (mAppWidgetIds) {
1251 ensureStateLoadedLocked();
1252 Provider p = lookupProviderLocked(provider);
1253 if (p != null && Binder.getCallingUid() == p.uid) {
1254 return getAppWidgetIds(p);
1255 } else {
1256 return new int[0];
1257 }
1258 }
1259 }
1260
1261 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1262 Provider p = null;
1263
1264 ActivityInfo activityInfo = ri.activityInfo;
1265 XmlResourceParser parser = null;
1266 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001267 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001268 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1269 if (parser == null) {
1270 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1271 + " meta-data for " + "AppWidget provider '" + component + '\'');
1272 return null;
1273 }
1274
1275 AttributeSet attrs = Xml.asAttributeSet(parser);
1276
1277 int type;
1278 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1279 && type != XmlPullParser.START_TAG) {
1280 // drain whitespace, comments, etc.
1281 }
1282
1283 String nodeName = parser.getName();
1284 if (!"appwidget-provider".equals(nodeName)) {
1285 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1286 + " AppWidget provider '" + component + '\'');
1287 return null;
1288 }
1289
1290 p = new Provider();
1291 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1292 info.provider = component;
1293 p.uid = activityInfo.applicationInfo.uid;
1294
Amith Yamasani483f3b02012-03-13 16:08:00 -07001295 Resources res = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001296 .getResourcesForApplication(activityInfo.applicationInfo);
1297
1298 TypedArray sa = res.obtainAttributes(attrs,
1299 com.android.internal.R.styleable.AppWidgetProviderInfo);
1300
1301 // These dimensions has to be resolved in the application's context.
1302 // We simply send back the raw complex data, which will be
1303 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1304 TypedValue value = sa
1305 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1306 info.minWidth = value != null ? value.data : 0;
1307 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1308 info.minHeight = value != null ? value.data : 0;
1309 value = sa.peekValue(
1310 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1311 info.minResizeWidth = value != null ? value.data : info.minWidth;
1312 value = sa.peekValue(
1313 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1314 info.minResizeHeight = value != null ? value.data : info.minHeight;
1315 info.updatePeriodMillis = sa.getInt(
1316 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1317 info.initialLayout = sa.getResourceId(
1318 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1319 String className = sa
1320 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1321 if (className != null) {
1322 info.configure = new ComponentName(component.getPackageName(), className);
1323 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001324 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001325 info.icon = ri.getIconResource();
1326 info.previewImage = sa.getResourceId(
1327 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1328 info.autoAdvanceViewId = sa.getResourceId(
1329 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1330 info.resizeMode = sa.getInt(
1331 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1332 AppWidgetProviderInfo.RESIZE_NONE);
1333
1334 sa.recycle();
1335 } catch (Exception e) {
1336 // Ok to catch Exception here, because anything going wrong because
1337 // of what a client process passes to us should not be fatal for the
1338 // system process.
1339 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1340 return null;
1341 } finally {
1342 if (parser != null)
1343 parser.close();
1344 }
1345 return p;
1346 }
1347
1348 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001349 PackageInfo pkgInfo = null;
1350 try {
1351 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1352 } catch (RemoteException re) {
1353 // Shouldn't happen, local call
1354 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001355 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1356 throw new PackageManager.NameNotFoundException();
1357 }
1358 return pkgInfo.applicationInfo.uid;
1359 }
1360
1361 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1362 int callingUid = Binder.getCallingUid();
1363 int packageUid;
1364 try {
1365 packageUid = getUidForPackage(packageName);
1366 } catch (PackageManager.NameNotFoundException ex) {
1367 throw new IllegalArgumentException("packageName and uid don't match packageName="
1368 + packageName);
1369 }
1370 if (!UserId.isSameApp(callingUid, packageUid)) {
1371 throw new IllegalArgumentException("packageName and uid don't match packageName="
1372 + packageName);
1373 }
1374 return callingUid;
1375 }
1376
1377 void sendInitialBroadcasts() {
1378 synchronized (mAppWidgetIds) {
1379 ensureStateLoadedLocked();
1380 final int N = mInstalledProviders.size();
1381 for (int i = 0; i < N; i++) {
1382 Provider p = mInstalledProviders.get(i);
1383 if (p.instances.size() > 0) {
1384 sendEnableIntentLocked(p);
1385 int[] appWidgetIds = getAppWidgetIds(p);
1386 sendUpdateIntentLocked(p, appWidgetIds);
1387 registerForBroadcastsLocked(p, appWidgetIds);
1388 }
1389 }
1390 }
1391 }
1392
1393 // only call from initialization -- it assumes that the data structures are all empty
1394 void loadStateLocked() {
1395 AtomicFile file = savedStateFile();
1396 try {
1397 FileInputStream stream = file.openRead();
1398 readStateFromFileLocked(stream);
1399
1400 if (stream != null) {
1401 try {
1402 stream.close();
1403 } catch (IOException e) {
1404 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1405 }
1406 }
1407 } catch (FileNotFoundException e) {
1408 Slog.w(TAG, "Failed to read state: " + e);
1409 }
1410 }
1411
1412 void saveStateLocked() {
1413 AtomicFile file = savedStateFile();
1414 FileOutputStream stream;
1415 try {
1416 stream = file.startWrite();
1417 if (writeStateToFileLocked(stream)) {
1418 file.finishWrite(stream);
1419 } else {
1420 file.failWrite(stream);
1421 Slog.w(TAG, "Failed to save state, restoring backup.");
1422 }
1423 } catch (IOException e) {
1424 Slog.w(TAG, "Failed open state file for write: " + e);
1425 }
1426 }
1427
1428 boolean writeStateToFileLocked(FileOutputStream stream) {
1429 int N;
1430
1431 try {
1432 XmlSerializer out = new FastXmlSerializer();
1433 out.setOutput(stream, "utf-8");
1434 out.startDocument(null, true);
1435 out.startTag(null, "gs");
1436
1437 int providerIndex = 0;
1438 N = mInstalledProviders.size();
1439 for (int i = 0; i < N; i++) {
1440 Provider p = mInstalledProviders.get(i);
1441 if (p.instances.size() > 0) {
1442 out.startTag(null, "p");
1443 out.attribute(null, "pkg", p.info.provider.getPackageName());
1444 out.attribute(null, "cl", p.info.provider.getClassName());
1445 out.endTag(null, "p");
1446 p.tag = providerIndex;
1447 providerIndex++;
1448 }
1449 }
1450
1451 N = mHosts.size();
1452 for (int i = 0; i < N; i++) {
1453 Host host = mHosts.get(i);
1454 out.startTag(null, "h");
1455 out.attribute(null, "pkg", host.packageName);
1456 out.attribute(null, "id", Integer.toHexString(host.hostId));
1457 out.endTag(null, "h");
1458 host.tag = i;
1459 }
1460
1461 N = mAppWidgetIds.size();
1462 for (int i = 0; i < N; i++) {
1463 AppWidgetId id = mAppWidgetIds.get(i);
1464 out.startTag(null, "g");
1465 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1466 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1467 if (id.provider != null) {
1468 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1469 }
1470 out.endTag(null, "g");
1471 }
1472
Michael Jurka61a5b012012-04-13 10:39:45 -07001473 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1474 while (it.hasNext()) {
1475 out.startTag(null, "b");
1476 out.attribute(null, "packageName", it.next());
1477 out.endTag(null, "b");
1478 }
1479
Amith Yamasani742a6712011-05-04 14:49:28 -07001480 out.endTag(null, "gs");
1481
1482 out.endDocument();
1483 return true;
1484 } catch (IOException e) {
1485 Slog.w(TAG, "Failed to write state: " + e);
1486 return false;
1487 }
1488 }
1489
1490 void readStateFromFileLocked(FileInputStream stream) {
1491 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001492 try {
1493 XmlPullParser parser = Xml.newPullParser();
1494 parser.setInput(stream, null);
1495
1496 int type;
1497 int providerIndex = 0;
1498 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1499 do {
1500 type = parser.next();
1501 if (type == XmlPullParser.START_TAG) {
1502 String tag = parser.getName();
1503 if ("p".equals(tag)) {
1504 // TODO: do we need to check that this package has the same signature
1505 // as before?
1506 String pkg = parser.getAttributeValue(null, "pkg");
1507 String cl = parser.getAttributeValue(null, "cl");
1508
1509 final PackageManager packageManager = mContext.getPackageManager();
1510 try {
1511 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1512 } catch (PackageManager.NameNotFoundException e) {
1513 String[] pkgs = packageManager
1514 .currentToCanonicalPackageNames(new String[] { pkg });
1515 pkg = pkgs[0];
1516 }
1517
1518 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1519 if (p == null && mSafeMode) {
1520 // if we're in safe mode, make a temporary one
1521 p = new Provider();
1522 p.info = new AppWidgetProviderInfo();
1523 p.info.provider = new ComponentName(pkg, cl);
1524 p.zombie = true;
1525 mInstalledProviders.add(p);
1526 }
1527 if (p != null) {
1528 // if it wasn't uninstalled or something
1529 loadedProviders.put(providerIndex, p);
1530 }
1531 providerIndex++;
1532 } else if ("h".equals(tag)) {
1533 Host host = new Host();
1534
1535 // TODO: do we need to check that this package has the same signature
1536 // as before?
1537 host.packageName = parser.getAttributeValue(null, "pkg");
1538 try {
1539 host.uid = getUidForPackage(host.packageName);
1540 } catch (PackageManager.NameNotFoundException ex) {
1541 host.zombie = true;
1542 }
1543 if (!host.zombie || mSafeMode) {
1544 // In safe mode, we don't discard the hosts we don't recognize
1545 // so that they're not pruned from our list. Otherwise, we do.
1546 host.hostId = Integer
1547 .parseInt(parser.getAttributeValue(null, "id"), 16);
1548 mHosts.add(host);
1549 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001550 } else if ("b".equals(tag)) {
1551 String packageName = parser.getAttributeValue(null, "packageName");
1552 if (packageName != null) {
1553 mPackagesWithBindWidgetPermission.add(packageName);
1554 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001555 } else if ("g".equals(tag)) {
1556 AppWidgetId id = new AppWidgetId();
1557 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1558 if (id.appWidgetId >= mNextAppWidgetId) {
1559 mNextAppWidgetId = id.appWidgetId + 1;
1560 }
1561
1562 String providerString = parser.getAttributeValue(null, "p");
1563 if (providerString != null) {
1564 // there's no provider if it hasn't been bound yet.
1565 // maybe we don't have to save this, but it brings the system
1566 // to the state it was in.
1567 int pIndex = Integer.parseInt(providerString, 16);
1568 id.provider = loadedProviders.get(pIndex);
1569 if (false) {
1570 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1571 + pIndex + " which is " + id.provider);
1572 }
1573 if (id.provider == null) {
1574 // This provider is gone. We just let the host figure out
1575 // that this happened when it fails to load it.
1576 continue;
1577 }
1578 }
1579
1580 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1581 id.host = mHosts.get(hIndex);
1582 if (id.host == null) {
1583 // This host is gone.
1584 continue;
1585 }
1586
1587 if (id.provider != null) {
1588 id.provider.instances.add(id);
1589 }
1590 id.host.instances.add(id);
1591 mAppWidgetIds.add(id);
1592 }
1593 }
1594 } while (type != XmlPullParser.END_DOCUMENT);
1595 success = true;
1596 } catch (NullPointerException e) {
1597 Slog.w(TAG, "failed parsing " + e);
1598 } catch (NumberFormatException e) {
1599 Slog.w(TAG, "failed parsing " + e);
1600 } catch (XmlPullParserException e) {
1601 Slog.w(TAG, "failed parsing " + e);
1602 } catch (IOException e) {
1603 Slog.w(TAG, "failed parsing " + e);
1604 } catch (IndexOutOfBoundsException e) {
1605 Slog.w(TAG, "failed parsing " + e);
1606 }
1607
1608 if (success) {
1609 // delete any hosts that didn't manage to get connected (should happen)
1610 // if it matters, they'll be reconnected.
1611 for (int i = mHosts.size() - 1; i >= 0; i--) {
1612 pruneHostLocked(mHosts.get(i));
1613 }
1614 } else {
1615 // failed reading, clean up
1616 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1617
1618 mAppWidgetIds.clear();
1619 mHosts.clear();
1620 final int N = mInstalledProviders.size();
1621 for (int i = 0; i < N; i++) {
1622 mInstalledProviders.get(i).instances.clear();
1623 }
1624 }
1625 }
1626
Amith Yamasani13593602012-03-22 16:16:17 -07001627 static File getSettingsFile(int userId) {
1628 return new File("/data/system/users/" + userId + "/" + SETTINGS_FILENAME);
1629 }
1630
Amith Yamasani742a6712011-05-04 14:49:28 -07001631 AtomicFile savedStateFile() {
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001632 File dir = new File("/data/system/users/" + mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001633 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001634 if (!settingsFile.exists() && mUserId == 0) {
1635 if (!dir.exists()) {
1636 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001637 }
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001638 // Migrate old data
1639 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1640 // Method doesn't throw an exception on failure. Ignore any errors
1641 // in moving the file (like non-existence)
1642 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001643 }
1644 return new AtomicFile(settingsFile);
1645 }
1646
Amith Yamasani13593602012-03-22 16:16:17 -07001647 void onUserRemoved() {
1648 // prune the ones we don't want to keep
1649 int N = mInstalledProviders.size();
1650 for (int i = N - 1; i >= 0; i--) {
1651 Provider p = mInstalledProviders.get(i);
1652 cancelBroadcasts(p);
1653 }
1654 getSettingsFile(mUserId).delete();
1655 }
1656
Winson Chung7fbd2842012-06-13 10:35:51 -07001657 boolean addProvidersForPackageLocked(String pkgName) {
1658 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001659 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1660 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001661 List<ResolveInfo> broadcastReceivers;
1662 try {
1663 broadcastReceivers = mPm.queryIntentReceivers(intent,
1664 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1665 PackageManager.GET_META_DATA, mUserId);
1666 } catch (RemoteException re) {
1667 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001668 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001669 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001670 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1671 for (int i = 0; i < N; i++) {
1672 ResolveInfo ri = broadcastReceivers.get(i);
1673 ActivityInfo ai = ri.activityInfo;
1674 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1675 continue;
1676 }
1677 if (pkgName.equals(ai.packageName)) {
1678 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001679 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001680 }
1681 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001682
1683 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001684 }
1685
Winson Chunga3195052012-06-25 10:02:10 -07001686 /**
1687 * Updates all providers with the specified package names, and records any providers that were
1688 * pruned.
1689 *
1690 * @return whether any providers were updated
1691 */
1692 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001693 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001694 HashSet<String> keep = new HashSet<String>();
1695 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1696 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001697 List<ResolveInfo> broadcastReceivers;
1698 try {
1699 broadcastReceivers = mPm.queryIntentReceivers(intent,
1700 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1701 PackageManager.GET_META_DATA, mUserId);
1702 } catch (RemoteException re) {
1703 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001704 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001705 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001706
1707 // add the missing ones and collect which ones to keep
1708 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1709 for (int i = 0; i < N; i++) {
1710 ResolveInfo ri = broadcastReceivers.get(i);
1711 ActivityInfo ai = ri.activityInfo;
1712 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1713 continue;
1714 }
1715 if (pkgName.equals(ai.packageName)) {
1716 ComponentName component = new ComponentName(ai.packageName, ai.name);
1717 Provider p = lookupProviderLocked(component);
1718 if (p == null) {
1719 if (addProviderLocked(ri)) {
1720 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001721 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001722 }
1723 } else {
1724 Provider parsed = parseProviderInfoXml(component, ri);
1725 if (parsed != null) {
1726 keep.add(ai.name);
1727 // Use the new AppWidgetProviderInfo.
1728 p.info = parsed.info;
1729 // If it's enabled
1730 final int M = p.instances.size();
1731 if (M > 0) {
1732 int[] appWidgetIds = getAppWidgetIds(p);
1733 // Reschedule for the new updatePeriodMillis (don't worry about handling
1734 // it specially if updatePeriodMillis didn't change because we just sent
1735 // an update, and the next one will be updatePeriodMillis from now).
1736 cancelBroadcasts(p);
1737 registerForBroadcastsLocked(p, appWidgetIds);
1738 // If it's currently showing, call back with the new
1739 // AppWidgetProviderInfo.
1740 for (int j = 0; j < M; j++) {
1741 AppWidgetId id = p.instances.get(j);
1742 id.views = null;
1743 if (id.host != null && id.host.callbacks != null) {
1744 try {
1745 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1746 } catch (RemoteException ex) {
1747 // It failed; remove the callback. No need to prune because
1748 // we know that this host is still referenced by this
1749 // instance.
1750 id.host.callbacks = null;
1751 }
1752 }
1753 }
1754 // Now that we've told the host, push out an update.
1755 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07001756 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001757 }
1758 }
1759 }
1760 }
1761 }
1762
1763 // prune the ones we don't want to keep
1764 N = mInstalledProviders.size();
1765 for (int i = N - 1; i >= 0; i--) {
1766 Provider p = mInstalledProviders.get(i);
1767 if (pkgName.equals(p.info.provider.getPackageName())
1768 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07001769 if (removedProviders != null) {
1770 removedProviders.add(p.info.provider);
1771 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001772 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001773 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001774 }
1775 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001776
1777 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07001778 }
1779
Winson Chung7fbd2842012-06-13 10:35:51 -07001780 boolean removeProvidersForPackageLocked(String pkgName) {
1781 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001782 int N = mInstalledProviders.size();
1783 for (int i = N - 1; i >= 0; i--) {
1784 Provider p = mInstalledProviders.get(i);
1785 if (pkgName.equals(p.info.provider.getPackageName())) {
1786 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001787 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001788 }
1789 }
1790
1791 // Delete the hosts for this package too
1792 //
1793 // By now, we have removed any AppWidgets that were in any hosts here,
1794 // so we don't need to worry about sending DISABLE broadcasts to them.
1795 N = mHosts.size();
1796 for (int i = N - 1; i >= 0; i--) {
1797 Host host = mHosts.get(i);
1798 if (pkgName.equals(host.packageName)) {
1799 deleteHostLocked(host);
1800 }
1801 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001802
1803 return providersRemoved;
1804 }
1805
1806 void notifyHostsForProvidersChangedLocked() {
1807 final int N = mHosts.size();
1808 for (int i = N - 1; i >= 0; i--) {
1809 Host host = mHosts.get(i);
1810 try {
1811 if (host.callbacks != null) {
1812 host.callbacks.providersChanged();
1813 }
1814 } catch (RemoteException ex) {
1815 // It failed; remove the callback. No need to prune because
1816 // we know that this host is still referenced by this
1817 // instance.
1818 host.callbacks = null;
1819 }
1820 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001821 }
1822}