blob: 77b3b5042cab5ba7452b1d0482a626422ca70539 [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();
Winson Chunge92aad42012-06-22 14:11:47 -0700193 // Cap memory usage at 1.5 times the size of the display
194 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
195 mMaxWidgetBitmapMemory = 6 * width * height;
Amith Yamasani742a6712011-05-04 14:49:28 -0700196 }
197
198 public void systemReady(boolean safeMode) {
199 mSafeMode = safeMode;
200
201 synchronized (mAppWidgetIds) {
202 ensureStateLoadedLocked();
203 }
204 }
205
206 void onConfigurationChanged() {
207 Locale revised = Locale.getDefault();
208 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
209 mLocale = revised;
210
211 synchronized (mAppWidgetIds) {
212 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700213 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
214 // list of installed providers and skip providers that we don't need to update.
215 // Also note that remove the provider does not clear the Provider component data.
216 ArrayList<Provider> installedProviders =
217 new ArrayList<Provider>(mInstalledProviders);
218 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
219 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700220 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700221 Provider p = installedProviders.get(i);
222 ComponentName cn = p.info.provider;
223 if (!removedProviders.contains(cn)) {
224 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
225 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700226 }
227 saveStateLocked();
228 }
229 }
230 }
231
232 void onBroadcastReceived(Intent intent) {
233 final String action = intent.getAction();
234 boolean added = false;
235 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700236 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700237 String pkgList[] = null;
238 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
239 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
240 added = true;
241 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
242 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
243 added = false;
244 } else {
245 Uri uri = intent.getData();
246 if (uri == null) {
247 return;
248 }
249 String pkgName = uri.getSchemeSpecificPart();
250 if (pkgName == null) {
251 return;
252 }
253 pkgList = new String[] { pkgName };
254 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
255 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
256 }
257 if (pkgList == null || pkgList.length == 0) {
258 return;
259 }
260 if (added || changed) {
261 synchronized (mAppWidgetIds) {
262 ensureStateLoadedLocked();
263 Bundle extras = intent.getExtras();
264 if (changed
265 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
266 for (String pkgName : pkgList) {
267 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700268 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700269 }
270 } else {
271 // The package was just added
272 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700273 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700274 }
275 }
276 saveStateLocked();
277 }
278 } else {
279 Bundle extras = intent.getExtras();
280 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
281 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
282 } else {
283 synchronized (mAppWidgetIds) {
284 ensureStateLoadedLocked();
285 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700286 providersModified |= removeProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700287 saveStateLocked();
288 }
289 }
290 }
291 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700292
293 if (providersModified) {
294 // If the set of providers has been modified, notify each active AppWidgetHost
295 synchronized (mAppWidgetIds) {
296 ensureStateLoadedLocked();
297 notifyHostsForProvidersChangedLocked();
298 }
299 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700300 }
301
302 private void dumpProvider(Provider p, int index, PrintWriter pw) {
303 AppWidgetProviderInfo info = p.info;
304 pw.print(" ["); pw.print(index); pw.print("] provider ");
305 pw.print(info.provider.flattenToShortString());
306 pw.println(':');
307 pw.print(" min=("); pw.print(info.minWidth);
308 pw.print("x"); pw.print(info.minHeight);
309 pw.print(") minResize=("); pw.print(info.minResizeWidth);
310 pw.print("x"); pw.print(info.minResizeHeight);
311 pw.print(") updatePeriodMillis=");
312 pw.print(info.updatePeriodMillis);
313 pw.print(" resizeMode=");
314 pw.print(info.resizeMode);
315 pw.print(" autoAdvanceViewId=");
316 pw.print(info.autoAdvanceViewId);
317 pw.print(" initialLayout=#");
318 pw.print(Integer.toHexString(info.initialLayout));
319 pw.print(" zombie="); pw.println(p.zombie);
320 }
321
322 private void dumpHost(Host host, int index, PrintWriter pw) {
323 pw.print(" ["); pw.print(index); pw.print("] hostId=");
324 pw.print(host.hostId); pw.print(' ');
325 pw.print(host.packageName); pw.print('/');
326 pw.print(host.uid); pw.println(':');
327 pw.print(" callbacks="); pw.println(host.callbacks);
328 pw.print(" instances.size="); pw.print(host.instances.size());
329 pw.print(" zombie="); pw.println(host.zombie);
330 }
331
332 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
333 pw.print(" ["); pw.print(index); pw.print("] id=");
334 pw.println(id.appWidgetId);
335 pw.print(" hostId=");
336 pw.print(id.host.hostId); pw.print(' ');
337 pw.print(id.host.packageName); pw.print('/');
338 pw.println(id.host.uid);
339 if (id.provider != null) {
340 pw.print(" provider=");
341 pw.println(id.provider.info.provider.flattenToShortString());
342 }
343 if (id.host != null) {
344 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
345 }
346 if (id.views != null) {
347 pw.print(" views="); pw.println(id.views);
348 }
349 }
350
351 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
352 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
353 != PackageManager.PERMISSION_GRANTED) {
354 pw.println("Permission Denial: can't dump from from pid="
355 + Binder.getCallingPid()
356 + ", uid=" + Binder.getCallingUid());
357 return;
358 }
359
360 synchronized (mAppWidgetIds) {
361 int N = mInstalledProviders.size();
362 pw.println("Providers:");
363 for (int i=0; i<N; i++) {
364 dumpProvider(mInstalledProviders.get(i), i, pw);
365 }
366
367 N = mAppWidgetIds.size();
368 pw.println(" ");
369 pw.println("AppWidgetIds:");
370 for (int i=0; i<N; i++) {
371 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
372 }
373
374 N = mHosts.size();
375 pw.println(" ");
376 pw.println("Hosts:");
377 for (int i=0; i<N; i++) {
378 dumpHost(mHosts.get(i), i, pw);
379 }
380
381 N = mDeletedProviders.size();
382 pw.println(" ");
383 pw.println("Deleted Providers:");
384 for (int i=0; i<N; i++) {
385 dumpProvider(mDeletedProviders.get(i), i, pw);
386 }
387
388 N = mDeletedHosts.size();
389 pw.println(" ");
390 pw.println("Deleted Hosts:");
391 for (int i=0; i<N; i++) {
392 dumpHost(mDeletedHosts.get(i), i, pw);
393 }
394 }
395 }
396
397 private void ensureStateLoadedLocked() {
398 if (!mStateLoaded) {
399 loadAppWidgetList();
400 loadStateLocked();
401 mStateLoaded = true;
402 }
403 }
404
405 public int allocateAppWidgetId(String packageName, int hostId) {
406 int callingUid = enforceCallingUid(packageName);
407 synchronized (mAppWidgetIds) {
408 ensureStateLoadedLocked();
409 int appWidgetId = mNextAppWidgetId++;
410
411 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
412
413 AppWidgetId id = new AppWidgetId();
414 id.appWidgetId = appWidgetId;
415 id.host = host;
416
417 host.instances.add(id);
418 mAppWidgetIds.add(id);
419
420 saveStateLocked();
421
422 return appWidgetId;
423 }
424 }
425
426 public void deleteAppWidgetId(int appWidgetId) {
427 synchronized (mAppWidgetIds) {
428 ensureStateLoadedLocked();
429 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
430 if (id != null) {
431 deleteAppWidgetLocked(id);
432 saveStateLocked();
433 }
434 }
435 }
436
437 public void deleteHost(int hostId) {
438 synchronized (mAppWidgetIds) {
439 ensureStateLoadedLocked();
440 int callingUid = Binder.getCallingUid();
441 Host host = lookupHostLocked(callingUid, hostId);
442 if (host != null) {
443 deleteHostLocked(host);
444 saveStateLocked();
445 }
446 }
447 }
448
449 public void deleteAllHosts() {
450 synchronized (mAppWidgetIds) {
451 ensureStateLoadedLocked();
452 int callingUid = Binder.getCallingUid();
453 final int N = mHosts.size();
454 boolean changed = false;
455 for (int i = N - 1; i >= 0; i--) {
456 Host host = mHosts.get(i);
457 if (host.uid == callingUid) {
458 deleteHostLocked(host);
459 changed = true;
460 }
461 }
462 if (changed) {
463 saveStateLocked();
464 }
465 }
466 }
467
468 void deleteHostLocked(Host host) {
469 final int N = host.instances.size();
470 for (int i = N - 1; i >= 0; i--) {
471 AppWidgetId id = host.instances.get(i);
472 deleteAppWidgetLocked(id);
473 }
474 host.instances.clear();
475 mHosts.remove(host);
476 mDeletedHosts.add(host);
477 // it's gone or going away, abruptly drop the callback connection
478 host.callbacks = null;
479 }
480
481 void deleteAppWidgetLocked(AppWidgetId id) {
482 // We first unbind all services that are bound to this id
483 unbindAppWidgetRemoteViewsServicesLocked(id);
484
485 Host host = id.host;
486 host.instances.remove(id);
487 pruneHostLocked(host);
488
489 mAppWidgetIds.remove(id);
490
491 Provider p = id.provider;
492 if (p != null) {
493 p.instances.remove(id);
494 if (!p.zombie) {
495 // send the broacast saying that this appWidgetId has been deleted
496 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
497 intent.setComponent(p.info.provider);
498 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Amith Yamasani67cf7d32012-02-16 14:31:23 -0800499 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700500 if (p.instances.size() == 0) {
501 // cancel the future updates
502 cancelBroadcasts(p);
503
504 // send the broacast saying that the provider is not in use any more
505 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
506 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -0800507 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700508 }
509 }
510 }
511 }
512
513 void cancelBroadcasts(Provider p) {
514 if (p.broadcast != null) {
515 mAlarmManager.cancel(p.broadcast);
516 long token = Binder.clearCallingIdentity();
517 try {
518 p.broadcast.cancel();
519 } finally {
520 Binder.restoreCallingIdentity(token);
521 }
522 p.broadcast = null;
523 }
524 }
525
Michael Jurka61a5b012012-04-13 10:39:45 -0700526 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700527 final long ident = Binder.clearCallingIdentity();
528 try {
529 synchronized (mAppWidgetIds) {
530 ensureStateLoadedLocked();
531 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
532 if (id == null) {
533 throw new IllegalArgumentException("bad appWidgetId");
534 }
535 if (id.provider != null) {
536 throw new IllegalArgumentException("appWidgetId " + appWidgetId
537 + " already bound to " + id.provider.info.provider);
538 }
539 Provider p = lookupProviderLocked(provider);
540 if (p == null) {
541 throw new IllegalArgumentException("not a appwidget provider: " + provider);
542 }
543 if (p.zombie) {
544 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
545 + " safe mode: " + provider);
546 }
547
Amith Yamasani742a6712011-05-04 14:49:28 -0700548 id.provider = p;
549 p.instances.add(id);
550 int instancesSize = p.instances.size();
551 if (instancesSize == 1) {
552 // tell the provider that it's ready
553 sendEnableIntentLocked(p);
554 }
555
556 // send an update now -- We need this update now, and just for this appWidgetId.
557 // It's less critical when the next one happens, so when we schedule the next one,
558 // we add updatePeriodMillis to its start time. That time will have some slop,
559 // but that's okay.
560 sendUpdateIntentLocked(p, new int[] { appWidgetId });
561
562 // schedule the future updates
563 registerForBroadcastsLocked(p, getAppWidgetIds(p));
564 saveStateLocked();
565 }
566 } finally {
567 Binder.restoreCallingIdentity(ident);
568 }
569 }
570
Michael Jurka61a5b012012-04-13 10:39:45 -0700571 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
572 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
573 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
574 bindAppWidgetIdImpl(appWidgetId, provider);
575 }
576
577 public boolean bindAppWidgetIdIfAllowed(
578 String packageName, int appWidgetId, ComponentName provider) {
579 try {
580 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
581 } catch (SecurityException se) {
582 if (!callerHasBindAppWidgetPermission(packageName)) {
583 return false;
584 }
585 }
586 bindAppWidgetIdImpl(appWidgetId, provider);
587 return true;
588 }
589
590 private boolean callerHasBindAppWidgetPermission(String packageName) {
591 int callingUid = Binder.getCallingUid();
592 try {
593 if (!UserId.isSameApp(callingUid, getUidForPackage(packageName))) {
594 return false;
595 }
596 } catch (Exception e) {
597 return false;
598 }
599 synchronized (mAppWidgetIds) {
600 ensureStateLoadedLocked();
601 return mPackagesWithBindWidgetPermission.contains(packageName);
602 }
603 }
604
605 public boolean hasBindAppWidgetPermission(String packageName) {
606 mContext.enforceCallingPermission(
607 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
608 "hasBindAppWidgetPermission packageName=" + packageName);
609
610 synchronized (mAppWidgetIds) {
611 ensureStateLoadedLocked();
612 return mPackagesWithBindWidgetPermission.contains(packageName);
613 }
614 }
615
616 public void setBindAppWidgetPermission(String packageName, boolean permission) {
617 mContext.enforceCallingPermission(
618 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
619 "setBindAppWidgetPermission packageName=" + packageName);
620
621 synchronized (mAppWidgetIds) {
622 ensureStateLoadedLocked();
623 if (permission) {
624 mPackagesWithBindWidgetPermission.add(packageName);
625 } else {
626 mPackagesWithBindWidgetPermission.remove(packageName);
627 }
628 }
629 saveStateLocked();
630 }
631
Amith Yamasani742a6712011-05-04 14:49:28 -0700632 // Binds to a specific RemoteViewsService
633 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
634 synchronized (mAppWidgetIds) {
635 ensureStateLoadedLocked();
636 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
637 if (id == null) {
638 throw new IllegalArgumentException("bad appWidgetId");
639 }
640 final ComponentName componentName = intent.getComponent();
641 try {
642 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
643 PackageManager.GET_PERMISSIONS);
644 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
645 throw new SecurityException("Selected service does not require "
646 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
647 }
648 } catch (PackageManager.NameNotFoundException e) {
649 throw new IllegalArgumentException("Unknown component " + componentName);
650 }
651
652 // If there is already a connection made for this service intent, then disconnect from
653 // that first. (This does not allow multiple connections to the same service under
654 // the same key)
655 ServiceConnectionProxy conn = null;
656 FilterComparison fc = new FilterComparison(intent);
657 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
658 if (mBoundRemoteViewsServices.containsKey(key)) {
659 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
660 conn.disconnect();
661 mContext.unbindService(conn);
662 mBoundRemoteViewsServices.remove(key);
663 }
664
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800665 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700666 // Bind to the RemoteViewsService (which will trigger a callback to the
667 // RemoteViewsAdapter.onServiceConnected())
668 final long token = Binder.clearCallingIdentity();
669 try {
670 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800671 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700672 mBoundRemoteViewsServices.put(key, conn);
673 } finally {
674 Binder.restoreCallingIdentity(token);
675 }
676
677 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
678 // when we can call back to the RemoteViewsService later to destroy associated
679 // factories.
680 incrementAppWidgetServiceRefCount(appWidgetId, fc);
681 }
682 }
683
684 // Unbinds from a specific RemoteViewsService
685 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
686 synchronized (mAppWidgetIds) {
687 ensureStateLoadedLocked();
688 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
689 // RemoteViewsAdapter)
690 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
691 intent));
692 if (mBoundRemoteViewsServices.containsKey(key)) {
693 // We don't need to use the appWidgetId until after we are sure there is something
694 // to unbind. Note that this may mask certain issues with apps calling unbind()
695 // more than necessary.
696 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
697 if (id == null) {
698 throw new IllegalArgumentException("bad appWidgetId");
699 }
700
701 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
702 .get(key);
703 conn.disconnect();
704 mContext.unbindService(conn);
705 mBoundRemoteViewsServices.remove(key);
706 } else {
707 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
708 }
709 }
710 }
711
712 // Unbinds from a RemoteViewsService when we delete an app widget
713 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
714 int appWidgetId = id.appWidgetId;
715 // Unbind all connections to Services bound to this AppWidgetId
716 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
717 .iterator();
718 while (it.hasNext()) {
719 final Pair<Integer, Intent.FilterComparison> key = it.next();
720 if (key.first.intValue() == appWidgetId) {
721 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
722 .get(key);
723 conn.disconnect();
724 mContext.unbindService(conn);
725 it.remove();
726 }
727 }
728
729 // Check if we need to destroy any services (if no other app widgets are
730 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800731 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700732 }
733
734 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800735 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700736 final ServiceConnection conn = new ServiceConnection() {
737 @Override
738 public void onServiceConnected(ComponentName name, IBinder service) {
739 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
740 try {
741 cb.onDestroy(intent);
742 } catch (RemoteException e) {
743 e.printStackTrace();
744 } catch (RuntimeException e) {
745 e.printStackTrace();
746 }
747 mContext.unbindService(this);
748 }
749
750 @Override
751 public void onServiceDisconnected(android.content.ComponentName name) {
752 // Do nothing
753 }
754 };
755
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800756 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700757 // Bind to the service and remove the static intent->factory mapping in the
758 // RemoteViewsService.
759 final long token = Binder.clearCallingIdentity();
760 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800761 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700762 } finally {
763 Binder.restoreCallingIdentity(token);
764 }
765 }
766
767 // Adds to the ref-count for a given RemoteViewsService intent
768 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
769 HashSet<Integer> appWidgetIds = null;
770 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
771 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
772 } else {
773 appWidgetIds = new HashSet<Integer>();
774 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
775 }
776 appWidgetIds.add(appWidgetId);
777 }
778
779 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
780 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800781 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700782 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
783 while (it.hasNext()) {
784 final FilterComparison key = it.next();
785 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800786 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700787 // If we have removed the last app widget referencing this service, then we
788 // should destroy it and remove it from this set
789 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800790 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700791 it.remove();
792 }
793 }
794 }
795 }
796
797 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
798 synchronized (mAppWidgetIds) {
799 ensureStateLoadedLocked();
800 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
801 if (id != null && id.provider != null && !id.provider.zombie) {
802 return id.provider.info;
803 }
804 return null;
805 }
806 }
807
808 public RemoteViews getAppWidgetViews(int appWidgetId) {
809 synchronized (mAppWidgetIds) {
810 ensureStateLoadedLocked();
811 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
812 if (id != null) {
813 return id.views;
814 }
815 return null;
816 }
817 }
818
819 public List<AppWidgetProviderInfo> getInstalledProviders() {
820 synchronized (mAppWidgetIds) {
821 ensureStateLoadedLocked();
822 final int N = mInstalledProviders.size();
823 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
824 for (int i = 0; i < N; i++) {
825 Provider p = mInstalledProviders.get(i);
826 if (!p.zombie) {
827 result.add(p.info);
828 }
829 }
830 return result;
831 }
832 }
833
834 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
835 if (appWidgetIds == null) {
836 return;
837 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700838
Adam Cohenf08a8b72012-07-16 12:02:10 -0700839 int bitmapMemoryUsage = 0;
840 if (views != null) {
841 bitmapMemoryUsage = views.estimateMemoryUsage();
842 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700843 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
844 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
845 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
846 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
847 " fill the device's screen once.");
848 }
849
Amith Yamasani742a6712011-05-04 14:49:28 -0700850 if (appWidgetIds.length == 0) {
851 return;
852 }
853 final int N = appWidgetIds.length;
854
855 synchronized (mAppWidgetIds) {
856 ensureStateLoadedLocked();
857 for (int i = 0; i < N; i++) {
858 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
859 updateAppWidgetInstanceLocked(id, views);
860 }
861 }
862 }
863
Adam Cohend2097eb2012-05-01 18:10:28 -0700864 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700865 synchronized (mAppWidgetIds) {
866 ensureStateLoadedLocked();
867 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
868
869 if (id == null) {
870 return;
871 }
872 Provider p = id.provider;
Adam Cohend2097eb2012-05-01 18:10:28 -0700873 id.options = options;
Adam Cohene8724c82012-04-19 17:11:40 -0700874
875 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700876 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700877 intent.setComponent(p.info.provider);
878 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700879 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
Adam Cohene8724c82012-04-19 17:11:40 -0700880 mContext.sendBroadcast(intent, mUserId);
881 }
882 }
883
Adam Cohend2097eb2012-05-01 18:10:28 -0700884 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -0700885 synchronized (mAppWidgetIds) {
886 ensureStateLoadedLocked();
887 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700888 if (id != null && id.options != null) {
889 return id.options;
Adam Cohene8724c82012-04-19 17:11:40 -0700890 } else {
891 return Bundle.EMPTY;
892 }
893 }
894 }
895
Amith Yamasani742a6712011-05-04 14:49:28 -0700896 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
897 if (appWidgetIds == null) {
898 return;
899 }
900 if (appWidgetIds.length == 0) {
901 return;
902 }
903 final int N = appWidgetIds.length;
904
905 synchronized (mAppWidgetIds) {
906 ensureStateLoadedLocked();
907 for (int i = 0; i < N; i++) {
908 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
909 updateAppWidgetInstanceLocked(id, views, true);
910 }
911 }
912 }
913
914 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
915 if (appWidgetIds == null) {
916 return;
917 }
918 if (appWidgetIds.length == 0) {
919 return;
920 }
921 final int N = appWidgetIds.length;
922
923 synchronized (mAppWidgetIds) {
924 ensureStateLoadedLocked();
925 for (int i = 0; i < N; i++) {
926 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
927 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
928 }
929 }
930 }
931
932 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
933 synchronized (mAppWidgetIds) {
934 ensureStateLoadedLocked();
935 Provider p = lookupProviderLocked(provider);
936 if (p == null) {
937 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
938 return;
939 }
940 ArrayList<AppWidgetId> instances = p.instances;
941 final int callingUid = Binder.getCallingUid();
942 final int N = instances.size();
943 for (int i = 0; i < N; i++) {
944 AppWidgetId id = instances.get(i);
945 if (canAccessAppWidgetId(id, callingUid)) {
946 updateAppWidgetInstanceLocked(id, views);
947 }
948 }
949 }
950 }
951
952 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
953 updateAppWidgetInstanceLocked(id, views, false);
954 }
955
956 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
957 // allow for stale appWidgetIds and other badness
958 // lookup also checks that the calling process can access the appWidgetId
959 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
960 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
961
962 // We do not want to save this RemoteViews
963 if (!isPartialUpdate)
964 id.views = views;
965
966 // is anyone listening?
967 if (id.host.callbacks != null) {
968 try {
969 // the lock is held, but this is a oneway call
970 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
971 } catch (RemoteException e) {
972 // It failed; remove the callback. No need to prune because
973 // we know that this host is still referenced by this instance.
974 id.host.callbacks = null;
975 }
976 }
977 }
978 }
979
980 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
981 // allow for stale appWidgetIds and other badness
982 // lookup also checks that the calling process can access the appWidgetId
983 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
984 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
985 // is anyone listening?
986 if (id.host.callbacks != null) {
987 try {
988 // the lock is held, but this is a oneway call
989 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
990 } catch (RemoteException e) {
991 // It failed; remove the callback. No need to prune because
992 // we know that this host is still referenced by this instance.
993 id.host.callbacks = null;
994 }
995 }
996
997 // If the host is unavailable, then we call the associated
998 // RemoteViewsFactory.onDataSetChanged() directly
999 if (id.host.callbacks == null) {
1000 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1001 for (FilterComparison key : keys) {
1002 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1003 Intent intent = key.getIntent();
1004
1005 final ServiceConnection conn = new ServiceConnection() {
1006 @Override
1007 public void onServiceConnected(ComponentName name, IBinder service) {
1008 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1009 .asInterface(service);
1010 try {
1011 cb.onDataSetChangedAsync();
1012 } catch (RemoteException e) {
1013 e.printStackTrace();
1014 } catch (RuntimeException e) {
1015 e.printStackTrace();
1016 }
1017 mContext.unbindService(this);
1018 }
1019
1020 @Override
1021 public void onServiceDisconnected(android.content.ComponentName name) {
1022 // Do nothing
1023 }
1024 };
1025
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001026 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001027 // Bind to the service and call onDataSetChanged()
1028 final long token = Binder.clearCallingIdentity();
1029 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001030 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001031 } finally {
1032 Binder.restoreCallingIdentity(token);
1033 }
1034 }
1035 }
1036 }
1037 }
1038 }
1039
1040 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1041 List<RemoteViews> updatedViews) {
1042 int callingUid = enforceCallingUid(packageName);
1043 synchronized (mAppWidgetIds) {
1044 ensureStateLoadedLocked();
1045 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1046 host.callbacks = callbacks;
1047
1048 updatedViews.clear();
1049
1050 ArrayList<AppWidgetId> instances = host.instances;
1051 int N = instances.size();
1052 int[] updatedIds = new int[N];
1053 for (int i = 0; i < N; i++) {
1054 AppWidgetId id = instances.get(i);
1055 updatedIds[i] = id.appWidgetId;
1056 updatedViews.add(id.views);
1057 }
1058 return updatedIds;
1059 }
1060 }
1061
1062 public void stopListening(int hostId) {
1063 synchronized (mAppWidgetIds) {
1064 ensureStateLoadedLocked();
1065 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1066 if (host != null) {
1067 host.callbacks = null;
1068 pruneHostLocked(host);
1069 }
1070 }
1071 }
1072
1073 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1074 if (id.host.uid == callingUid) {
1075 // Apps hosting the AppWidget have access to it.
1076 return true;
1077 }
1078 if (id.provider != null && id.provider.uid == callingUid) {
1079 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1080 return true;
1081 }
1082 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1083 // Apps that can bind have access to all appWidgetIds.
1084 return true;
1085 }
1086 // Nobody else can access it.
1087 return false;
1088 }
1089
1090 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1091 int callingUid = Binder.getCallingUid();
1092 final int N = mAppWidgetIds.size();
1093 for (int i = 0; i < N; i++) {
1094 AppWidgetId id = mAppWidgetIds.get(i);
1095 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1096 return id;
1097 }
1098 }
1099 return null;
1100 }
1101
1102 Provider lookupProviderLocked(ComponentName provider) {
1103 final int N = mInstalledProviders.size();
1104 for (int i = 0; i < N; i++) {
1105 Provider p = mInstalledProviders.get(i);
1106 if (p.info.provider.equals(provider)) {
1107 return p;
1108 }
1109 }
1110 return null;
1111 }
1112
1113 Host lookupHostLocked(int uid, int hostId) {
1114 final int N = mHosts.size();
1115 for (int i = 0; i < N; i++) {
1116 Host h = mHosts.get(i);
1117 if (h.uid == uid && h.hostId == hostId) {
1118 return h;
1119 }
1120 }
1121 return null;
1122 }
1123
1124 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1125 final int N = mHosts.size();
1126 for (int i = 0; i < N; i++) {
1127 Host h = mHosts.get(i);
1128 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1129 return h;
1130 }
1131 }
1132 Host host = new Host();
1133 host.packageName = packageName;
1134 host.uid = uid;
1135 host.hostId = hostId;
1136 mHosts.add(host);
1137 return host;
1138 }
1139
1140 void pruneHostLocked(Host host) {
1141 if (host.instances.size() == 0 && host.callbacks == null) {
1142 mHosts.remove(host);
1143 }
1144 }
1145
1146 void loadAppWidgetList() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001147 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001148 try {
1149 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1150 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1151 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001152
Amith Yamasani483f3b02012-03-13 16:08:00 -07001153 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1154 for (int i = 0; i < N; i++) {
1155 ResolveInfo ri = broadcastReceivers.get(i);
1156 addProviderLocked(ri);
1157 }
1158 } catch (RemoteException re) {
1159 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001160 }
1161 }
1162
1163 boolean addProviderLocked(ResolveInfo ri) {
1164 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1165 return false;
1166 }
1167 if (!ri.activityInfo.isEnabled()) {
1168 return false;
1169 }
1170 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1171 ri.activityInfo.name), ri);
1172 if (p != null) {
1173 mInstalledProviders.add(p);
1174 return true;
1175 } else {
1176 return false;
1177 }
1178 }
1179
1180 void removeProviderLocked(int index, Provider p) {
1181 int N = p.instances.size();
1182 for (int i = 0; i < N; i++) {
1183 AppWidgetId id = p.instances.get(i);
1184 // Call back with empty RemoteViews
1185 updateAppWidgetInstanceLocked(id, null);
1186 // Stop telling the host about updates for this from now on
1187 cancelBroadcasts(p);
1188 // clear out references to this appWidgetId
1189 id.host.instances.remove(id);
1190 mAppWidgetIds.remove(id);
1191 id.provider = null;
1192 pruneHostLocked(id.host);
1193 id.host = null;
1194 }
1195 p.instances.clear();
1196 mInstalledProviders.remove(index);
1197 mDeletedProviders.add(p);
1198 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1199 cancelBroadcasts(p);
1200 }
1201
1202 void sendEnableIntentLocked(Provider p) {
1203 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1204 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001205 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001206 }
1207
1208 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1209 if (appWidgetIds != null && appWidgetIds.length > 0) {
1210 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1211 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1212 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001213 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001214 }
1215 }
1216
1217 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1218 if (p.info.updatePeriodMillis > 0) {
1219 // if this is the first instance, set the alarm. otherwise,
1220 // rely on the fact that we've already set it and that
1221 // PendingIntent.getBroadcast will update the extras.
1222 boolean alreadyRegistered = p.broadcast != null;
1223 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1224 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1225 intent.setComponent(p.info.provider);
1226 long token = Binder.clearCallingIdentity();
1227 try {
1228 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1229 PendingIntent.FLAG_UPDATE_CURRENT);
1230 } finally {
1231 Binder.restoreCallingIdentity(token);
1232 }
1233 if (!alreadyRegistered) {
1234 long period = p.info.updatePeriodMillis;
1235 if (period < MIN_UPDATE_PERIOD) {
1236 period = MIN_UPDATE_PERIOD;
1237 }
1238 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1239 .elapsedRealtime()
1240 + period, period, p.broadcast);
1241 }
1242 }
1243 }
1244
1245 static int[] getAppWidgetIds(Provider p) {
1246 int instancesSize = p.instances.size();
1247 int appWidgetIds[] = new int[instancesSize];
1248 for (int i = 0; i < instancesSize; i++) {
1249 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1250 }
1251 return appWidgetIds;
1252 }
1253
1254 public int[] getAppWidgetIds(ComponentName provider) {
1255 synchronized (mAppWidgetIds) {
1256 ensureStateLoadedLocked();
1257 Provider p = lookupProviderLocked(provider);
1258 if (p != null && Binder.getCallingUid() == p.uid) {
1259 return getAppWidgetIds(p);
1260 } else {
1261 return new int[0];
1262 }
1263 }
1264 }
1265
1266 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1267 Provider p = null;
1268
1269 ActivityInfo activityInfo = ri.activityInfo;
1270 XmlResourceParser parser = null;
1271 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001272 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001273 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1274 if (parser == null) {
1275 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1276 + " meta-data for " + "AppWidget provider '" + component + '\'');
1277 return null;
1278 }
1279
1280 AttributeSet attrs = Xml.asAttributeSet(parser);
1281
1282 int type;
1283 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1284 && type != XmlPullParser.START_TAG) {
1285 // drain whitespace, comments, etc.
1286 }
1287
1288 String nodeName = parser.getName();
1289 if (!"appwidget-provider".equals(nodeName)) {
1290 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1291 + " AppWidget provider '" + component + '\'');
1292 return null;
1293 }
1294
1295 p = new Provider();
1296 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1297 info.provider = component;
1298 p.uid = activityInfo.applicationInfo.uid;
1299
Amith Yamasani483f3b02012-03-13 16:08:00 -07001300 Resources res = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001301 .getResourcesForApplication(activityInfo.applicationInfo);
1302
1303 TypedArray sa = res.obtainAttributes(attrs,
1304 com.android.internal.R.styleable.AppWidgetProviderInfo);
1305
1306 // These dimensions has to be resolved in the application's context.
1307 // We simply send back the raw complex data, which will be
1308 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1309 TypedValue value = sa
1310 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1311 info.minWidth = value != null ? value.data : 0;
1312 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1313 info.minHeight = value != null ? value.data : 0;
1314 value = sa.peekValue(
1315 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1316 info.minResizeWidth = value != null ? value.data : info.minWidth;
1317 value = sa.peekValue(
1318 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1319 info.minResizeHeight = value != null ? value.data : info.minHeight;
1320 info.updatePeriodMillis = sa.getInt(
1321 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1322 info.initialLayout = sa.getResourceId(
1323 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1324 String className = sa
1325 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1326 if (className != null) {
1327 info.configure = new ComponentName(component.getPackageName(), className);
1328 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001329 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001330 info.icon = ri.getIconResource();
1331 info.previewImage = sa.getResourceId(
1332 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1333 info.autoAdvanceViewId = sa.getResourceId(
1334 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1335 info.resizeMode = sa.getInt(
1336 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1337 AppWidgetProviderInfo.RESIZE_NONE);
1338
1339 sa.recycle();
1340 } catch (Exception e) {
1341 // Ok to catch Exception here, because anything going wrong because
1342 // of what a client process passes to us should not be fatal for the
1343 // system process.
1344 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1345 return null;
1346 } finally {
1347 if (parser != null)
1348 parser.close();
1349 }
1350 return p;
1351 }
1352
1353 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001354 PackageInfo pkgInfo = null;
1355 try {
1356 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1357 } catch (RemoteException re) {
1358 // Shouldn't happen, local call
1359 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001360 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1361 throw new PackageManager.NameNotFoundException();
1362 }
1363 return pkgInfo.applicationInfo.uid;
1364 }
1365
1366 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1367 int callingUid = Binder.getCallingUid();
1368 int packageUid;
1369 try {
1370 packageUid = getUidForPackage(packageName);
1371 } catch (PackageManager.NameNotFoundException ex) {
1372 throw new IllegalArgumentException("packageName and uid don't match packageName="
1373 + packageName);
1374 }
1375 if (!UserId.isSameApp(callingUid, packageUid)) {
1376 throw new IllegalArgumentException("packageName and uid don't match packageName="
1377 + packageName);
1378 }
1379 return callingUid;
1380 }
1381
1382 void sendInitialBroadcasts() {
1383 synchronized (mAppWidgetIds) {
1384 ensureStateLoadedLocked();
1385 final int N = mInstalledProviders.size();
1386 for (int i = 0; i < N; i++) {
1387 Provider p = mInstalledProviders.get(i);
1388 if (p.instances.size() > 0) {
1389 sendEnableIntentLocked(p);
1390 int[] appWidgetIds = getAppWidgetIds(p);
1391 sendUpdateIntentLocked(p, appWidgetIds);
1392 registerForBroadcastsLocked(p, appWidgetIds);
1393 }
1394 }
1395 }
1396 }
1397
1398 // only call from initialization -- it assumes that the data structures are all empty
1399 void loadStateLocked() {
1400 AtomicFile file = savedStateFile();
1401 try {
1402 FileInputStream stream = file.openRead();
1403 readStateFromFileLocked(stream);
1404
1405 if (stream != null) {
1406 try {
1407 stream.close();
1408 } catch (IOException e) {
1409 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1410 }
1411 }
1412 } catch (FileNotFoundException e) {
1413 Slog.w(TAG, "Failed to read state: " + e);
1414 }
1415 }
1416
1417 void saveStateLocked() {
1418 AtomicFile file = savedStateFile();
1419 FileOutputStream stream;
1420 try {
1421 stream = file.startWrite();
1422 if (writeStateToFileLocked(stream)) {
1423 file.finishWrite(stream);
1424 } else {
1425 file.failWrite(stream);
1426 Slog.w(TAG, "Failed to save state, restoring backup.");
1427 }
1428 } catch (IOException e) {
1429 Slog.w(TAG, "Failed open state file for write: " + e);
1430 }
1431 }
1432
1433 boolean writeStateToFileLocked(FileOutputStream stream) {
1434 int N;
1435
1436 try {
1437 XmlSerializer out = new FastXmlSerializer();
1438 out.setOutput(stream, "utf-8");
1439 out.startDocument(null, true);
1440 out.startTag(null, "gs");
1441
1442 int providerIndex = 0;
1443 N = mInstalledProviders.size();
1444 for (int i = 0; i < N; i++) {
1445 Provider p = mInstalledProviders.get(i);
1446 if (p.instances.size() > 0) {
1447 out.startTag(null, "p");
1448 out.attribute(null, "pkg", p.info.provider.getPackageName());
1449 out.attribute(null, "cl", p.info.provider.getClassName());
1450 out.endTag(null, "p");
1451 p.tag = providerIndex;
1452 providerIndex++;
1453 }
1454 }
1455
1456 N = mHosts.size();
1457 for (int i = 0; i < N; i++) {
1458 Host host = mHosts.get(i);
1459 out.startTag(null, "h");
1460 out.attribute(null, "pkg", host.packageName);
1461 out.attribute(null, "id", Integer.toHexString(host.hostId));
1462 out.endTag(null, "h");
1463 host.tag = i;
1464 }
1465
1466 N = mAppWidgetIds.size();
1467 for (int i = 0; i < N; i++) {
1468 AppWidgetId id = mAppWidgetIds.get(i);
1469 out.startTag(null, "g");
1470 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1471 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1472 if (id.provider != null) {
1473 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1474 }
1475 out.endTag(null, "g");
1476 }
1477
Michael Jurka61a5b012012-04-13 10:39:45 -07001478 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1479 while (it.hasNext()) {
1480 out.startTag(null, "b");
1481 out.attribute(null, "packageName", it.next());
1482 out.endTag(null, "b");
1483 }
1484
Amith Yamasani742a6712011-05-04 14:49:28 -07001485 out.endTag(null, "gs");
1486
1487 out.endDocument();
1488 return true;
1489 } catch (IOException e) {
1490 Slog.w(TAG, "Failed to write state: " + e);
1491 return false;
1492 }
1493 }
1494
1495 void readStateFromFileLocked(FileInputStream stream) {
1496 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001497 try {
1498 XmlPullParser parser = Xml.newPullParser();
1499 parser.setInput(stream, null);
1500
1501 int type;
1502 int providerIndex = 0;
1503 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1504 do {
1505 type = parser.next();
1506 if (type == XmlPullParser.START_TAG) {
1507 String tag = parser.getName();
1508 if ("p".equals(tag)) {
1509 // TODO: do we need to check that this package has the same signature
1510 // as before?
1511 String pkg = parser.getAttributeValue(null, "pkg");
1512 String cl = parser.getAttributeValue(null, "cl");
1513
1514 final PackageManager packageManager = mContext.getPackageManager();
1515 try {
1516 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1517 } catch (PackageManager.NameNotFoundException e) {
1518 String[] pkgs = packageManager
1519 .currentToCanonicalPackageNames(new String[] { pkg });
1520 pkg = pkgs[0];
1521 }
1522
1523 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1524 if (p == null && mSafeMode) {
1525 // if we're in safe mode, make a temporary one
1526 p = new Provider();
1527 p.info = new AppWidgetProviderInfo();
1528 p.info.provider = new ComponentName(pkg, cl);
1529 p.zombie = true;
1530 mInstalledProviders.add(p);
1531 }
1532 if (p != null) {
1533 // if it wasn't uninstalled or something
1534 loadedProviders.put(providerIndex, p);
1535 }
1536 providerIndex++;
1537 } else if ("h".equals(tag)) {
1538 Host host = new Host();
1539
1540 // TODO: do we need to check that this package has the same signature
1541 // as before?
1542 host.packageName = parser.getAttributeValue(null, "pkg");
1543 try {
1544 host.uid = getUidForPackage(host.packageName);
1545 } catch (PackageManager.NameNotFoundException ex) {
1546 host.zombie = true;
1547 }
1548 if (!host.zombie || mSafeMode) {
1549 // In safe mode, we don't discard the hosts we don't recognize
1550 // so that they're not pruned from our list. Otherwise, we do.
1551 host.hostId = Integer
1552 .parseInt(parser.getAttributeValue(null, "id"), 16);
1553 mHosts.add(host);
1554 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001555 } else if ("b".equals(tag)) {
1556 String packageName = parser.getAttributeValue(null, "packageName");
1557 if (packageName != null) {
1558 mPackagesWithBindWidgetPermission.add(packageName);
1559 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001560 } else if ("g".equals(tag)) {
1561 AppWidgetId id = new AppWidgetId();
1562 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1563 if (id.appWidgetId >= mNextAppWidgetId) {
1564 mNextAppWidgetId = id.appWidgetId + 1;
1565 }
1566
1567 String providerString = parser.getAttributeValue(null, "p");
1568 if (providerString != null) {
1569 // there's no provider if it hasn't been bound yet.
1570 // maybe we don't have to save this, but it brings the system
1571 // to the state it was in.
1572 int pIndex = Integer.parseInt(providerString, 16);
1573 id.provider = loadedProviders.get(pIndex);
1574 if (false) {
1575 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1576 + pIndex + " which is " + id.provider);
1577 }
1578 if (id.provider == null) {
1579 // This provider is gone. We just let the host figure out
1580 // that this happened when it fails to load it.
1581 continue;
1582 }
1583 }
1584
1585 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1586 id.host = mHosts.get(hIndex);
1587 if (id.host == null) {
1588 // This host is gone.
1589 continue;
1590 }
1591
1592 if (id.provider != null) {
1593 id.provider.instances.add(id);
1594 }
1595 id.host.instances.add(id);
1596 mAppWidgetIds.add(id);
1597 }
1598 }
1599 } while (type != XmlPullParser.END_DOCUMENT);
1600 success = true;
1601 } catch (NullPointerException e) {
1602 Slog.w(TAG, "failed parsing " + e);
1603 } catch (NumberFormatException e) {
1604 Slog.w(TAG, "failed parsing " + e);
1605 } catch (XmlPullParserException e) {
1606 Slog.w(TAG, "failed parsing " + e);
1607 } catch (IOException e) {
1608 Slog.w(TAG, "failed parsing " + e);
1609 } catch (IndexOutOfBoundsException e) {
1610 Slog.w(TAG, "failed parsing " + e);
1611 }
1612
1613 if (success) {
1614 // delete any hosts that didn't manage to get connected (should happen)
1615 // if it matters, they'll be reconnected.
1616 for (int i = mHosts.size() - 1; i >= 0; i--) {
1617 pruneHostLocked(mHosts.get(i));
1618 }
1619 } else {
1620 // failed reading, clean up
1621 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1622
1623 mAppWidgetIds.clear();
1624 mHosts.clear();
1625 final int N = mInstalledProviders.size();
1626 for (int i = 0; i < N; i++) {
1627 mInstalledProviders.get(i).instances.clear();
1628 }
1629 }
1630 }
1631
Amith Yamasani13593602012-03-22 16:16:17 -07001632 static File getSettingsFile(int userId) {
1633 return new File("/data/system/users/" + userId + "/" + SETTINGS_FILENAME);
1634 }
1635
Amith Yamasani742a6712011-05-04 14:49:28 -07001636 AtomicFile savedStateFile() {
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001637 File dir = new File("/data/system/users/" + mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001638 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001639 if (!settingsFile.exists() && mUserId == 0) {
1640 if (!dir.exists()) {
1641 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001642 }
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001643 // Migrate old data
1644 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1645 // Method doesn't throw an exception on failure. Ignore any errors
1646 // in moving the file (like non-existence)
1647 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001648 }
1649 return new AtomicFile(settingsFile);
1650 }
1651
Amith Yamasani13593602012-03-22 16:16:17 -07001652 void onUserRemoved() {
1653 // prune the ones we don't want to keep
1654 int N = mInstalledProviders.size();
1655 for (int i = N - 1; i >= 0; i--) {
1656 Provider p = mInstalledProviders.get(i);
1657 cancelBroadcasts(p);
1658 }
1659 getSettingsFile(mUserId).delete();
1660 }
1661
Winson Chung7fbd2842012-06-13 10:35:51 -07001662 boolean addProvidersForPackageLocked(String pkgName) {
1663 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001664 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1665 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001666 List<ResolveInfo> broadcastReceivers;
1667 try {
1668 broadcastReceivers = mPm.queryIntentReceivers(intent,
1669 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1670 PackageManager.GET_META_DATA, mUserId);
1671 } catch (RemoteException re) {
1672 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001673 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001674 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001675 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1676 for (int i = 0; i < N; i++) {
1677 ResolveInfo ri = broadcastReceivers.get(i);
1678 ActivityInfo ai = ri.activityInfo;
1679 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1680 continue;
1681 }
1682 if (pkgName.equals(ai.packageName)) {
1683 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001684 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001685 }
1686 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001687
1688 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001689 }
1690
Winson Chunga3195052012-06-25 10:02:10 -07001691 /**
1692 * Updates all providers with the specified package names, and records any providers that were
1693 * pruned.
1694 *
1695 * @return whether any providers were updated
1696 */
1697 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001698 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001699 HashSet<String> keep = new HashSet<String>();
1700 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1701 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001702 List<ResolveInfo> broadcastReceivers;
1703 try {
1704 broadcastReceivers = mPm.queryIntentReceivers(intent,
1705 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1706 PackageManager.GET_META_DATA, mUserId);
1707 } catch (RemoteException re) {
1708 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001709 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001710 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001711
1712 // add the missing ones and collect which ones to keep
1713 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1714 for (int i = 0; i < N; i++) {
1715 ResolveInfo ri = broadcastReceivers.get(i);
1716 ActivityInfo ai = ri.activityInfo;
1717 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1718 continue;
1719 }
1720 if (pkgName.equals(ai.packageName)) {
1721 ComponentName component = new ComponentName(ai.packageName, ai.name);
1722 Provider p = lookupProviderLocked(component);
1723 if (p == null) {
1724 if (addProviderLocked(ri)) {
1725 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001726 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001727 }
1728 } else {
1729 Provider parsed = parseProviderInfoXml(component, ri);
1730 if (parsed != null) {
1731 keep.add(ai.name);
1732 // Use the new AppWidgetProviderInfo.
1733 p.info = parsed.info;
1734 // If it's enabled
1735 final int M = p.instances.size();
1736 if (M > 0) {
1737 int[] appWidgetIds = getAppWidgetIds(p);
1738 // Reschedule for the new updatePeriodMillis (don't worry about handling
1739 // it specially if updatePeriodMillis didn't change because we just sent
1740 // an update, and the next one will be updatePeriodMillis from now).
1741 cancelBroadcasts(p);
1742 registerForBroadcastsLocked(p, appWidgetIds);
1743 // If it's currently showing, call back with the new
1744 // AppWidgetProviderInfo.
1745 for (int j = 0; j < M; j++) {
1746 AppWidgetId id = p.instances.get(j);
1747 id.views = null;
1748 if (id.host != null && id.host.callbacks != null) {
1749 try {
1750 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1751 } catch (RemoteException ex) {
1752 // It failed; remove the callback. No need to prune because
1753 // we know that this host is still referenced by this
1754 // instance.
1755 id.host.callbacks = null;
1756 }
1757 }
1758 }
1759 // Now that we've told the host, push out an update.
1760 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07001761 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001762 }
1763 }
1764 }
1765 }
1766 }
1767
1768 // prune the ones we don't want to keep
1769 N = mInstalledProviders.size();
1770 for (int i = N - 1; i >= 0; i--) {
1771 Provider p = mInstalledProviders.get(i);
1772 if (pkgName.equals(p.info.provider.getPackageName())
1773 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07001774 if (removedProviders != null) {
1775 removedProviders.add(p.info.provider);
1776 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001777 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001778 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001779 }
1780 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001781
1782 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07001783 }
1784
Winson Chung7fbd2842012-06-13 10:35:51 -07001785 boolean removeProvidersForPackageLocked(String pkgName) {
1786 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001787 int N = mInstalledProviders.size();
1788 for (int i = N - 1; i >= 0; i--) {
1789 Provider p = mInstalledProviders.get(i);
1790 if (pkgName.equals(p.info.provider.getPackageName())) {
1791 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001792 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001793 }
1794 }
1795
1796 // Delete the hosts for this package too
1797 //
1798 // By now, we have removed any AppWidgets that were in any hosts here,
1799 // so we don't need to worry about sending DISABLE broadcasts to them.
1800 N = mHosts.size();
1801 for (int i = N - 1; i >= 0; i--) {
1802 Host host = mHosts.get(i);
1803 if (pkgName.equals(host.packageName)) {
1804 deleteHostLocked(host);
1805 }
1806 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001807
1808 return providersRemoved;
1809 }
1810
1811 void notifyHostsForProvidersChangedLocked() {
1812 final int N = mHosts.size();
1813 for (int i = N - 1; i >= 0; i--) {
1814 Host host = mHosts.get(i);
1815 try {
1816 if (host.callbacks != null) {
1817 host.callbacks.providersChanged();
1818 }
1819 } catch (RemoteException ex) {
1820 // It failed; remove the callback. No need to prune because
1821 // we know that this host is still referenced by this
1822 // instance.
1823 host.callbacks = null;
1824 }
1825 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001826 }
1827}