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