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