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