blob: d1829ab8edcca3a0b8ca613fd826a0cf2a4b4369 [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;
Adam Cohena1a2f962012-11-01 14:06:16 -070044import android.os.Handler;
45import android.os.HandlerThread;
Amith Yamasani742a6712011-05-04 14:49:28 -070046import android.os.IBinder;
Adam Cohena1a2f962012-11-01 14:06:16 -070047import android.os.Looper;
Jim Millerf229e4d2012-09-12 20:32:50 -070048import android.os.Process;
Amith Yamasani742a6712011-05-04 14:49:28 -070049import android.os.RemoteException;
50import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070051import android.os.UserHandle;
Dianne Hackborn39606a02012-07-31 17:54:35 -070052import android.util.AtomicFile;
Amith Yamasani742a6712011-05-04 14:49:28 -070053import android.util.AttributeSet;
54import android.util.Log;
55import android.util.Pair;
56import android.util.Slog;
57import android.util.TypedValue;
58import android.util.Xml;
Jeff Browna8b9def2012-07-23 14:22:49 -070059import android.view.Display;
Adam Cohen311c79c2012-05-10 14:44:38 -070060import android.view.WindowManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070061import android.widget.RemoteViews;
62
63import com.android.internal.appwidget.IAppWidgetHost;
Amith Yamasani742a6712011-05-04 14:49:28 -070064import com.android.internal.util.FastXmlSerializer;
65import com.android.internal.widget.IRemoteViewsAdapterConnection;
66import com.android.internal.widget.IRemoteViewsFactory;
Amith Yamasani742a6712011-05-04 14:49:28 -070067
68import org.xmlpull.v1.XmlPullParser;
69import org.xmlpull.v1.XmlPullParserException;
70import org.xmlpull.v1.XmlSerializer;
71
72import java.io.File;
73import java.io.FileDescriptor;
74import java.io.FileInputStream;
75import java.io.FileNotFoundException;
76import java.io.FileOutputStream;
77import java.io.IOException;
78import java.io.PrintWriter;
79import java.util.ArrayList;
80import java.util.HashMap;
81import java.util.HashSet;
82import java.util.Iterator;
83import java.util.List;
84import java.util.Locale;
85import java.util.Set;
86
87class AppWidgetServiceImpl {
88
89 private static final String TAG = "AppWidgetServiceImpl";
90 private static final String SETTINGS_FILENAME = "appwidgets.xml";
91 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
92
Amith Yamasani8320de82012-10-05 16:10:38 -070093 private static boolean DBG = false;
94
Amith Yamasani742a6712011-05-04 14:49:28 -070095 /*
96 * When identifying a Host or Provider based on the calling process, use the uid field. When
97 * identifying a Host or Provider based on a package manager broadcast, use the package given.
98 */
99
100 static class Provider {
101 int uid;
102 AppWidgetProviderInfo info;
103 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
104 PendingIntent broadcast;
105 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
106
107 int tag; // for use while saving state (the index)
108 }
109
110 static class Host {
111 int uid;
112 int hostId;
113 String packageName;
114 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
115 IAppWidgetHost callbacks;
116 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
117
118 int tag; // for use while saving state (the index)
Amith Yamasanic566b432012-11-30 15:26:21 -0800119
120 boolean uidMatches(int callingUid) {
121 if (UserHandle.getAppId(callingUid) == Process.myUid()) {
122 // For a host that's in the system process, ignore the user id
123 return UserHandle.isSameApp(this.uid, callingUid);
124 } else {
125 return this.uid == callingUid;
126 }
127 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700128 }
129
130 static class AppWidgetId {
131 int appWidgetId;
132 Provider provider;
133 RemoteViews views;
Adam Cohend2097eb2012-05-01 18:10:28 -0700134 Bundle options;
Amith Yamasani742a6712011-05-04 14:49:28 -0700135 Host host;
136 }
137
138 /**
139 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
140 * needs to be a static inner class since a reference to the ServiceConnection is held globally
141 * and may lead us to leak AppWidgetService instances (if there were more than one).
142 */
143 static class ServiceConnectionProxy implements ServiceConnection {
144 private final IBinder mConnectionCb;
145
146 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
147 mConnectionCb = connectionCb;
148 }
149
150 public void onServiceConnected(ComponentName name, IBinder service) {
151 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
152 .asInterface(mConnectionCb);
153 try {
154 cb.onServiceConnected(service);
155 } catch (Exception e) {
156 e.printStackTrace();
157 }
158 }
159
160 public void onServiceDisconnected(ComponentName name) {
161 disconnect();
162 }
163
164 public void disconnect() {
165 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
166 .asInterface(mConnectionCb);
167 try {
168 cb.onServiceDisconnected();
169 } catch (Exception e) {
170 e.printStackTrace();
171 }
172 }
173 }
174
175 // Manages active connections to RemoteViewsServices
176 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
177 // Manages persistent references to RemoteViewsServices from different App Widgets
178 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
179
180 Context mContext;
181 Locale mLocale;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700182 IPackageManager mPm;
Amith Yamasani742a6712011-05-04 14:49:28 -0700183 AlarmManager mAlarmManager;
184 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
185 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
186 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
187 ArrayList<Host> mHosts = new ArrayList<Host>();
Michael Jurka61a5b012012-04-13 10:39:45 -0700188 // set of package names
189 HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
Amith Yamasani742a6712011-05-04 14:49:28 -0700190 boolean mSafeMode;
191 int mUserId;
192 boolean mStateLoaded;
Adam Cohen311c79c2012-05-10 14:44:38 -0700193 int mMaxWidgetBitmapMemory;
Amith Yamasani742a6712011-05-04 14:49:28 -0700194
Adam Cohena1a2f962012-11-01 14:06:16 -0700195 private final Handler mSaveStateHandler;
196
Amith Yamasani742a6712011-05-04 14:49:28 -0700197 // These are for debugging only -- widgets are going missing in some rare instances
198 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
199 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
200
Adam Cohena1a2f962012-11-01 14:06:16 -0700201 AppWidgetServiceImpl(Context context, int userId, Handler saveStateHandler) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700202 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700203 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700204 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
205 mUserId = userId;
Adam Cohena1a2f962012-11-01 14:06:16 -0700206 mSaveStateHandler = saveStateHandler;
Adam Cohen311c79c2012-05-10 14:44:38 -0700207 computeMaximumWidgetBitmapMemory();
208 }
209
210 void computeMaximumWidgetBitmapMemory() {
211 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Jeff Browna8b9def2012-07-23 14:22:49 -0700212 Display display = wm.getDefaultDisplay();
213 Point size = new Point();
214 display.getRealSize(size);
Winson Chunge92aad42012-06-22 14:11:47 -0700215 // Cap memory usage at 1.5 times the size of the display
216 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
Jeff Browna8b9def2012-07-23 14:22:49 -0700217 mMaxWidgetBitmapMemory = 6 * size.x * size.y;
Amith Yamasani742a6712011-05-04 14:49:28 -0700218 }
219
220 public void systemReady(boolean safeMode) {
221 mSafeMode = safeMode;
222
223 synchronized (mAppWidgetIds) {
224 ensureStateLoadedLocked();
225 }
226 }
227
Amith Yamasani8320de82012-10-05 16:10:38 -0700228 private void log(String msg) {
229 Slog.i(TAG, "u=" + mUserId + ": " + msg);
230 }
231
Amith Yamasani742a6712011-05-04 14:49:28 -0700232 void onConfigurationChanged() {
Amith Yamasani8320de82012-10-05 16:10:38 -0700233 if (DBG) log("Got onConfigurationChanged()");
Amith Yamasani742a6712011-05-04 14:49:28 -0700234 Locale revised = Locale.getDefault();
235 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
236 mLocale = revised;
237
238 synchronized (mAppWidgetIds) {
239 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700240 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
241 // list of installed providers and skip providers that we don't need to update.
242 // Also note that remove the provider does not clear the Provider component data.
243 ArrayList<Provider> installedProviders =
244 new ArrayList<Provider>(mInstalledProviders);
245 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
246 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700247 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700248 Provider p = installedProviders.get(i);
249 ComponentName cn = p.info.provider;
250 if (!removedProviders.contains(cn)) {
251 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
252 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700253 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700254 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700255 }
256 }
257 }
258
259 void onBroadcastReceived(Intent intent) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700260 if (DBG) log("onBroadcast " + intent);
Amith Yamasani742a6712011-05-04 14:49:28 -0700261 final String action = intent.getAction();
262 boolean added = false;
263 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700264 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700265 String pkgList[] = null;
266 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
267 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
268 added = true;
269 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
270 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
271 added = false;
272 } else {
273 Uri uri = intent.getData();
274 if (uri == null) {
275 return;
276 }
277 String pkgName = uri.getSchemeSpecificPart();
278 if (pkgName == null) {
279 return;
280 }
281 pkgList = new String[] { pkgName };
282 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
283 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
284 }
285 if (pkgList == null || pkgList.length == 0) {
286 return;
287 }
288 if (added || changed) {
289 synchronized (mAppWidgetIds) {
290 ensureStateLoadedLocked();
291 Bundle extras = intent.getExtras();
292 if (changed
293 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
294 for (String pkgName : pkgList) {
295 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700296 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700297 }
298 } else {
299 // The package was just added
300 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700301 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700302 }
303 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700304 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700305 }
306 } else {
307 Bundle extras = intent.getExtras();
308 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
309 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
310 } else {
311 synchronized (mAppWidgetIds) {
312 ensureStateLoadedLocked();
313 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700314 providersModified |= removeProvidersForPackageLocked(pkgName);
Adam Cohena1a2f962012-11-01 14:06:16 -0700315 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700316 }
317 }
318 }
319 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700320
321 if (providersModified) {
322 // If the set of providers has been modified, notify each active AppWidgetHost
323 synchronized (mAppWidgetIds) {
324 ensureStateLoadedLocked();
325 notifyHostsForProvidersChangedLocked();
326 }
327 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700328 }
329
330 private void dumpProvider(Provider p, int index, PrintWriter pw) {
331 AppWidgetProviderInfo info = p.info;
332 pw.print(" ["); pw.print(index); pw.print("] provider ");
333 pw.print(info.provider.flattenToShortString());
334 pw.println(':');
335 pw.print(" min=("); pw.print(info.minWidth);
336 pw.print("x"); pw.print(info.minHeight);
337 pw.print(") minResize=("); pw.print(info.minResizeWidth);
338 pw.print("x"); pw.print(info.minResizeHeight);
339 pw.print(") updatePeriodMillis=");
340 pw.print(info.updatePeriodMillis);
341 pw.print(" resizeMode=");
342 pw.print(info.resizeMode);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700343 pw.print(info.widgetCategory);
Amith Yamasani742a6712011-05-04 14:49:28 -0700344 pw.print(" autoAdvanceViewId=");
345 pw.print(info.autoAdvanceViewId);
346 pw.print(" initialLayout=#");
347 pw.print(Integer.toHexString(info.initialLayout));
Amith Yamasani791f8772012-11-21 14:06:07 -0800348 pw.print(" uid="); pw.print(p.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700349 pw.print(" zombie="); pw.println(p.zombie);
350 }
351
352 private void dumpHost(Host host, int index, PrintWriter pw) {
353 pw.print(" ["); pw.print(index); pw.print("] hostId=");
354 pw.print(host.hostId); pw.print(' ');
355 pw.print(host.packageName); pw.print('/');
356 pw.print(host.uid); pw.println(':');
357 pw.print(" callbacks="); pw.println(host.callbacks);
358 pw.print(" instances.size="); pw.print(host.instances.size());
359 pw.print(" zombie="); pw.println(host.zombie);
360 }
361
362 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
363 pw.print(" ["); pw.print(index); pw.print("] id=");
364 pw.println(id.appWidgetId);
365 pw.print(" hostId=");
366 pw.print(id.host.hostId); pw.print(' ');
367 pw.print(id.host.packageName); pw.print('/');
368 pw.println(id.host.uid);
369 if (id.provider != null) {
370 pw.print(" provider=");
371 pw.println(id.provider.info.provider.flattenToShortString());
372 }
373 if (id.host != null) {
374 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
375 }
376 if (id.views != null) {
377 pw.print(" views="); pw.println(id.views);
378 }
379 }
380
381 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
382 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
383 != PackageManager.PERMISSION_GRANTED) {
384 pw.println("Permission Denial: can't dump from from pid="
385 + Binder.getCallingPid()
386 + ", uid=" + Binder.getCallingUid());
387 return;
388 }
389
390 synchronized (mAppWidgetIds) {
391 int N = mInstalledProviders.size();
392 pw.println("Providers:");
393 for (int i=0; i<N; i++) {
394 dumpProvider(mInstalledProviders.get(i), i, pw);
395 }
396
397 N = mAppWidgetIds.size();
398 pw.println(" ");
399 pw.println("AppWidgetIds:");
400 for (int i=0; i<N; i++) {
401 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
402 }
403
404 N = mHosts.size();
405 pw.println(" ");
406 pw.println("Hosts:");
407 for (int i=0; i<N; i++) {
408 dumpHost(mHosts.get(i), i, pw);
409 }
410
411 N = mDeletedProviders.size();
412 pw.println(" ");
413 pw.println("Deleted Providers:");
414 for (int i=0; i<N; i++) {
415 dumpProvider(mDeletedProviders.get(i), i, pw);
416 }
417
418 N = mDeletedHosts.size();
419 pw.println(" ");
420 pw.println("Deleted Hosts:");
421 for (int i=0; i<N; i++) {
422 dumpHost(mDeletedHosts.get(i), i, pw);
423 }
424 }
425 }
426
427 private void ensureStateLoadedLocked() {
428 if (!mStateLoaded) {
Adam Cohena1a2f962012-11-01 14:06:16 -0700429 loadAppWidgetListLocked();
Amith Yamasani742a6712011-05-04 14:49:28 -0700430 loadStateLocked();
431 mStateLoaded = true;
432 }
433 }
434
435 public int allocateAppWidgetId(String packageName, int hostId) {
Jim Millerf229e4d2012-09-12 20:32:50 -0700436 int callingUid = enforceSystemOrCallingUid(packageName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700437 synchronized (mAppWidgetIds) {
438 ensureStateLoadedLocked();
439 int appWidgetId = mNextAppWidgetId++;
440
441 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
442
443 AppWidgetId id = new AppWidgetId();
444 id.appWidgetId = appWidgetId;
445 id.host = host;
446
447 host.instances.add(id);
448 mAppWidgetIds.add(id);
449
Adam Cohena1a2f962012-11-01 14:06:16 -0700450 saveStateAsync();
Amith Yamasani8320de82012-10-05 16:10:38 -0700451 if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
452 + " id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700453 return appWidgetId;
454 }
455 }
456
457 public void deleteAppWidgetId(int appWidgetId) {
458 synchronized (mAppWidgetIds) {
459 ensureStateLoadedLocked();
460 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
461 if (id != null) {
462 deleteAppWidgetLocked(id);
Adam Cohena1a2f962012-11-01 14:06:16 -0700463 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700464 }
465 }
466 }
467
468 public void deleteHost(int hostId) {
469 synchronized (mAppWidgetIds) {
470 ensureStateLoadedLocked();
471 int callingUid = Binder.getCallingUid();
472 Host host = lookupHostLocked(callingUid, hostId);
473 if (host != null) {
474 deleteHostLocked(host);
Adam Cohena1a2f962012-11-01 14:06:16 -0700475 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700476 }
477 }
478 }
479
480 public void deleteAllHosts() {
481 synchronized (mAppWidgetIds) {
482 ensureStateLoadedLocked();
483 int callingUid = Binder.getCallingUid();
484 final int N = mHosts.size();
485 boolean changed = false;
486 for (int i = N - 1; i >= 0; i--) {
487 Host host = mHosts.get(i);
Amith Yamasanic566b432012-11-30 15:26:21 -0800488 if (host.uidMatches(callingUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700489 deleteHostLocked(host);
490 changed = true;
491 }
492 }
493 if (changed) {
Adam Cohena1a2f962012-11-01 14:06:16 -0700494 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700495 }
496 }
497 }
498
499 void deleteHostLocked(Host host) {
500 final int N = host.instances.size();
501 for (int i = N - 1; i >= 0; i--) {
502 AppWidgetId id = host.instances.get(i);
503 deleteAppWidgetLocked(id);
504 }
505 host.instances.clear();
506 mHosts.remove(host);
507 mDeletedHosts.add(host);
508 // it's gone or going away, abruptly drop the callback connection
509 host.callbacks = null;
510 }
511
512 void deleteAppWidgetLocked(AppWidgetId id) {
513 // We first unbind all services that are bound to this id
514 unbindAppWidgetRemoteViewsServicesLocked(id);
515
516 Host host = id.host;
517 host.instances.remove(id);
518 pruneHostLocked(host);
519
520 mAppWidgetIds.remove(id);
521
522 Provider p = id.provider;
523 if (p != null) {
524 p.instances.remove(id);
525 if (!p.zombie) {
526 // send the broacast saying that this appWidgetId has been deleted
527 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
528 intent.setComponent(p.info.provider);
529 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700530 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700531 if (p.instances.size() == 0) {
532 // cancel the future updates
533 cancelBroadcasts(p);
534
535 // send the broacast saying that the provider is not in use any more
536 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
537 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700538 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700539 }
540 }
541 }
542 }
543
544 void cancelBroadcasts(Provider p) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700545 if (DBG) log("cancelBroadcasts for " + p);
Amith Yamasani742a6712011-05-04 14:49:28 -0700546 if (p.broadcast != null) {
547 mAlarmManager.cancel(p.broadcast);
548 long token = Binder.clearCallingIdentity();
549 try {
550 p.broadcast.cancel();
551 } finally {
552 Binder.restoreCallingIdentity(token);
553 }
554 p.broadcast = null;
555 }
556 }
557
Adam Cohen0aa2d422012-09-07 17:37:26 -0700558 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700559 if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
560 + " provider=" + provider);
Amith Yamasani742a6712011-05-04 14:49:28 -0700561 final long ident = Binder.clearCallingIdentity();
562 try {
563 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700564 options = cloneIfLocalBinder(options);
Amith Yamasani742a6712011-05-04 14:49:28 -0700565 ensureStateLoadedLocked();
566 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
567 if (id == null) {
568 throw new IllegalArgumentException("bad appWidgetId");
569 }
570 if (id.provider != null) {
571 throw new IllegalArgumentException("appWidgetId " + appWidgetId
572 + " already bound to " + id.provider.info.provider);
573 }
574 Provider p = lookupProviderLocked(provider);
575 if (p == null) {
576 throw new IllegalArgumentException("not a appwidget provider: " + provider);
577 }
578 if (p.zombie) {
579 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
580 + " safe mode: " + provider);
581 }
582
Amith Yamasani742a6712011-05-04 14:49:28 -0700583 id.provider = p;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700584 if (options == null) {
585 options = new Bundle();
586 }
587 id.options = options;
588
589 // We need to provide a default value for the widget category if it is not specified
590 if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
591 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
592 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
593 }
594
Amith Yamasani742a6712011-05-04 14:49:28 -0700595 p.instances.add(id);
596 int instancesSize = p.instances.size();
597 if (instancesSize == 1) {
598 // tell the provider that it's ready
599 sendEnableIntentLocked(p);
600 }
601
602 // send an update now -- We need this update now, and just for this appWidgetId.
603 // It's less critical when the next one happens, so when we schedule the next one,
604 // we add updatePeriodMillis to its start time. That time will have some slop,
605 // but that's okay.
606 sendUpdateIntentLocked(p, new int[] { appWidgetId });
607
608 // schedule the future updates
609 registerForBroadcastsLocked(p, getAppWidgetIds(p));
Adam Cohena1a2f962012-11-01 14:06:16 -0700610 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700611 }
612 } finally {
613 Binder.restoreCallingIdentity(ident);
614 }
615 }
616
Adam Cohen0aa2d422012-09-07 17:37:26 -0700617 public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka67a871d2012-11-01 18:26:01 -0700618 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET,
Michael Jurka61a5b012012-04-13 10:39:45 -0700619 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700620 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700621 }
622
623 public boolean bindAppWidgetIdIfAllowed(
Adam Cohen0aa2d422012-09-07 17:37:26 -0700624 String packageName, int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700625 try {
Michael Jurka67a871d2012-11-01 18:26:01 -0700626 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
Michael Jurka61a5b012012-04-13 10:39:45 -0700627 } catch (SecurityException se) {
628 if (!callerHasBindAppWidgetPermission(packageName)) {
629 return false;
630 }
631 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700632 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700633 return true;
634 }
635
636 private boolean callerHasBindAppWidgetPermission(String packageName) {
637 int callingUid = Binder.getCallingUid();
638 try {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700639 if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700640 return false;
641 }
642 } catch (Exception e) {
643 return false;
644 }
645 synchronized (mAppWidgetIds) {
646 ensureStateLoadedLocked();
647 return mPackagesWithBindWidgetPermission.contains(packageName);
648 }
649 }
650
651 public boolean hasBindAppWidgetPermission(String packageName) {
652 mContext.enforceCallingPermission(
653 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
654 "hasBindAppWidgetPermission packageName=" + packageName);
655
656 synchronized (mAppWidgetIds) {
657 ensureStateLoadedLocked();
658 return mPackagesWithBindWidgetPermission.contains(packageName);
659 }
660 }
661
662 public void setBindAppWidgetPermission(String packageName, boolean permission) {
663 mContext.enforceCallingPermission(
664 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
665 "setBindAppWidgetPermission packageName=" + packageName);
666
667 synchronized (mAppWidgetIds) {
668 ensureStateLoadedLocked();
669 if (permission) {
670 mPackagesWithBindWidgetPermission.add(packageName);
671 } else {
672 mPackagesWithBindWidgetPermission.remove(packageName);
673 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700674 saveStateAsync();
Michael Jurka61a5b012012-04-13 10:39:45 -0700675 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700676 }
677
Amith Yamasani742a6712011-05-04 14:49:28 -0700678 // Binds to a specific RemoteViewsService
679 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
680 synchronized (mAppWidgetIds) {
681 ensureStateLoadedLocked();
682 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
683 if (id == null) {
684 throw new IllegalArgumentException("bad appWidgetId");
685 }
686 final ComponentName componentName = intent.getComponent();
687 try {
Amith Yamasani98edc952012-09-25 14:09:27 -0700688 final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
689 PackageManager.GET_PERMISSIONS, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700690 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
691 throw new SecurityException("Selected service does not require "
692 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
693 }
Amith Yamasani98edc952012-09-25 14:09:27 -0700694 } catch (RemoteException e) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700695 throw new IllegalArgumentException("Unknown component " + componentName);
696 }
697
698 // If there is already a connection made for this service intent, then disconnect from
699 // that first. (This does not allow multiple connections to the same service under
700 // the same key)
701 ServiceConnectionProxy conn = null;
702 FilterComparison fc = new FilterComparison(intent);
703 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
704 if (mBoundRemoteViewsServices.containsKey(key)) {
705 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
706 conn.disconnect();
707 mContext.unbindService(conn);
708 mBoundRemoteViewsServices.remove(key);
709 }
710
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700711 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani791f8772012-11-21 14:06:07 -0800712 if (userId != mUserId) {
713 Slog.w(TAG, "AppWidgetServiceImpl of user " + mUserId
714 + " binding to provider on user " + userId);
715 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700716 // Bind to the RemoteViewsService (which will trigger a callback to the
717 // RemoteViewsAdapter.onServiceConnected())
718 final long token = Binder.clearCallingIdentity();
719 try {
720 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani27b89e62013-01-16 12:30:11 -0800721 mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
722 new UserHandle(userId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700723 mBoundRemoteViewsServices.put(key, conn);
724 } finally {
725 Binder.restoreCallingIdentity(token);
726 }
727
728 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
729 // when we can call back to the RemoteViewsService later to destroy associated
730 // factories.
731 incrementAppWidgetServiceRefCount(appWidgetId, fc);
732 }
733 }
734
735 // Unbinds from a specific RemoteViewsService
736 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
737 synchronized (mAppWidgetIds) {
738 ensureStateLoadedLocked();
739 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
740 // RemoteViewsAdapter)
741 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
742 intent));
743 if (mBoundRemoteViewsServices.containsKey(key)) {
744 // We don't need to use the appWidgetId until after we are sure there is something
745 // to unbind. Note that this may mask certain issues with apps calling unbind()
746 // more than necessary.
747 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
748 if (id == null) {
749 throw new IllegalArgumentException("bad appWidgetId");
750 }
751
752 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
753 .get(key);
754 conn.disconnect();
755 mContext.unbindService(conn);
756 mBoundRemoteViewsServices.remove(key);
Amith Yamasani742a6712011-05-04 14:49:28 -0700757 }
758 }
759 }
760
761 // Unbinds from a RemoteViewsService when we delete an app widget
762 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
763 int appWidgetId = id.appWidgetId;
764 // Unbind all connections to Services bound to this AppWidgetId
765 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
766 .iterator();
767 while (it.hasNext()) {
768 final Pair<Integer, Intent.FilterComparison> key = it.next();
769 if (key.first.intValue() == appWidgetId) {
770 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
771 .get(key);
772 conn.disconnect();
773 mContext.unbindService(conn);
774 it.remove();
775 }
776 }
777
778 // Check if we need to destroy any services (if no other app widgets are
779 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800780 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700781 }
782
783 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800784 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700785 final ServiceConnection conn = new ServiceConnection() {
786 @Override
787 public void onServiceConnected(ComponentName name, IBinder service) {
788 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
789 try {
790 cb.onDestroy(intent);
791 } catch (RemoteException e) {
792 e.printStackTrace();
793 } catch (RuntimeException e) {
794 e.printStackTrace();
795 }
796 mContext.unbindService(this);
797 }
798
799 @Override
800 public void onServiceDisconnected(android.content.ComponentName name) {
801 // Do nothing
802 }
803 };
804
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700805 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700806 // Bind to the service and remove the static intent->factory mapping in the
807 // RemoteViewsService.
808 final long token = Binder.clearCallingIdentity();
809 try {
Amith Yamasani27b89e62013-01-16 12:30:11 -0800810 mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
811 new UserHandle(userId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700812 } finally {
813 Binder.restoreCallingIdentity(token);
814 }
815 }
816
817 // Adds to the ref-count for a given RemoteViewsService intent
818 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
819 HashSet<Integer> appWidgetIds = null;
820 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
821 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
822 } else {
823 appWidgetIds = new HashSet<Integer>();
824 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
825 }
826 appWidgetIds.add(appWidgetId);
827 }
828
829 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
830 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800831 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700832 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
833 while (it.hasNext()) {
834 final FilterComparison key = it.next();
835 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800836 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700837 // If we have removed the last app widget referencing this service, then we
838 // should destroy it and remove it from this set
839 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800840 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700841 it.remove();
842 }
843 }
844 }
845 }
846
847 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
848 synchronized (mAppWidgetIds) {
849 ensureStateLoadedLocked();
850 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
851 if (id != null && id.provider != null && !id.provider.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700852 return cloneIfLocalBinder(id.provider.info);
Amith Yamasani742a6712011-05-04 14:49:28 -0700853 }
854 return null;
855 }
856 }
857
858 public RemoteViews getAppWidgetViews(int appWidgetId) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700859 if (DBG) log("getAppWidgetViews id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700860 synchronized (mAppWidgetIds) {
861 ensureStateLoadedLocked();
862 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
863 if (id != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700864 return cloneIfLocalBinder(id.views);
Amith Yamasani742a6712011-05-04 14:49:28 -0700865 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700866 if (DBG) log(" couldn't find appwidgetid");
Amith Yamasani742a6712011-05-04 14:49:28 -0700867 return null;
868 }
869 }
870
Adam Cohend9e5af32012-11-28 16:34:57 -0800871 public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700872 synchronized (mAppWidgetIds) {
873 ensureStateLoadedLocked();
874 final int N = mInstalledProviders.size();
875 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
876 for (int i = 0; i < N; i++) {
877 Provider p = mInstalledProviders.get(i);
Adam Cohen3fcc6b22012-11-15 14:10:07 -0800878 if (!p.zombie && (p.info.widgetCategory & categoryFilter) != 0) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700879 result.add(cloneIfLocalBinder(p.info));
Amith Yamasani742a6712011-05-04 14:49:28 -0700880 }
881 }
882 return result;
883 }
884 }
885
886 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
887 if (appWidgetIds == null) {
888 return;
889 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700890 if (DBG) log("updateAppWidgetIds views: " + views);
Adam Cohenf08a8b72012-07-16 12:02:10 -0700891 int bitmapMemoryUsage = 0;
892 if (views != null) {
893 bitmapMemoryUsage = views.estimateMemoryUsage();
894 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700895 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
896 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
897 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
898 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
899 " fill the device's screen once.");
900 }
901
Amith Yamasani742a6712011-05-04 14:49:28 -0700902 if (appWidgetIds.length == 0) {
903 return;
904 }
905 final int N = appWidgetIds.length;
906
907 synchronized (mAppWidgetIds) {
908 ensureStateLoadedLocked();
909 for (int i = 0; i < N; i++) {
910 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
911 updateAppWidgetInstanceLocked(id, views);
912 }
913 }
914 }
915
Adam Cohena1a2f962012-11-01 14:06:16 -0700916 private void saveStateAsync() {
917 mSaveStateHandler.post(mSaveStateRunnable);
918 }
919
920 private final Runnable mSaveStateRunnable = new Runnable() {
921 @Override
922 public void run() {
923 synchronized (mAppWidgetIds) {
924 ensureStateLoadedLocked();
925 saveStateLocked();
926 }
927 }
928 };
929
Adam Cohend2097eb2012-05-01 18:10:28 -0700930 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700931 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700932 options = cloneIfLocalBinder(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700933 ensureStateLoadedLocked();
934 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
935
936 if (id == null) {
937 return;
938 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700939
Adam Cohene8724c82012-04-19 17:11:40 -0700940 Provider p = id.provider;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700941 // Merge the options
942 id.options.putAll(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700943
944 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700945 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700946 intent.setComponent(p.info.provider);
947 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700948 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700949 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Adam Cohena1a2f962012-11-01 14:06:16 -0700950 saveStateAsync();
Adam Cohene8724c82012-04-19 17:11:40 -0700951 }
952 }
953
Adam Cohend2097eb2012-05-01 18:10:28 -0700954 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -0700955 synchronized (mAppWidgetIds) {
956 ensureStateLoadedLocked();
957 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700958 if (id != null && id.options != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700959 return cloneIfLocalBinder(id.options);
Adam Cohene8724c82012-04-19 17:11:40 -0700960 } else {
961 return Bundle.EMPTY;
962 }
963 }
964 }
965
Amith Yamasani742a6712011-05-04 14:49:28 -0700966 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
967 if (appWidgetIds == null) {
968 return;
969 }
970 if (appWidgetIds.length == 0) {
971 return;
972 }
973 final int N = appWidgetIds.length;
974
975 synchronized (mAppWidgetIds) {
976 ensureStateLoadedLocked();
977 for (int i = 0; i < N; i++) {
978 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Amith Yamasani791f8772012-11-21 14:06:07 -0800979 if (id == null) {
Amith Yamasanic566b432012-11-30 15:26:21 -0800980 Slog.w(TAG, "widget id " + appWidgetIds[i] + " not found!");
981 } else if (id.views != null) {
Winson Chung66119882012-10-11 14:26:25 -0700982 // Only trigger a partial update for a widget if it has received a full update
983 updateAppWidgetInstanceLocked(id, views, true);
984 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700985 }
986 }
987 }
988
989 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
990 if (appWidgetIds == null) {
991 return;
992 }
993 if (appWidgetIds.length == 0) {
994 return;
995 }
996 final int N = appWidgetIds.length;
997
998 synchronized (mAppWidgetIds) {
999 ensureStateLoadedLocked();
1000 for (int i = 0; i < N; i++) {
1001 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
1002 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
1003 }
1004 }
1005 }
1006
1007 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
1008 synchronized (mAppWidgetIds) {
1009 ensureStateLoadedLocked();
1010 Provider p = lookupProviderLocked(provider);
1011 if (p == null) {
1012 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
1013 return;
1014 }
1015 ArrayList<AppWidgetId> instances = p.instances;
1016 final int callingUid = Binder.getCallingUid();
1017 final int N = instances.size();
1018 for (int i = 0; i < N; i++) {
1019 AppWidgetId id = instances.get(i);
1020 if (canAccessAppWidgetId(id, callingUid)) {
1021 updateAppWidgetInstanceLocked(id, views);
1022 }
1023 }
1024 }
1025 }
1026
1027 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
1028 updateAppWidgetInstanceLocked(id, views, false);
1029 }
1030
1031 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
1032 // allow for stale appWidgetIds and other badness
1033 // lookup also checks that the calling process can access the appWidgetId
1034 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1035 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1036
Winson Chung66119882012-10-11 14:26:25 -07001037 if (!isPartialUpdate) {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001038 // For a full update we replace the RemoteViews completely.
Amith Yamasani742a6712011-05-04 14:49:28 -07001039 id.views = views;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001040 } else {
1041 // For a partial update, we merge the new RemoteViews with the old.
1042 id.views.mergeRemoteViews(views);
1043 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001044
1045 // is anyone listening?
1046 if (id.host.callbacks != null) {
1047 try {
1048 // the lock is held, but this is a oneway call
1049 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
1050 } catch (RemoteException e) {
1051 // It failed; remove the callback. No need to prune because
1052 // we know that this host is still referenced by this instance.
1053 id.host.callbacks = null;
1054 }
1055 }
1056 }
1057 }
1058
1059 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1060 // allow for stale appWidgetIds and other badness
1061 // lookup also checks that the calling process can access the appWidgetId
1062 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1063 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1064 // is anyone listening?
1065 if (id.host.callbacks != null) {
1066 try {
1067 // the lock is held, but this is a oneway call
1068 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1069 } catch (RemoteException e) {
1070 // It failed; remove the callback. No need to prune because
1071 // we know that this host is still referenced by this instance.
1072 id.host.callbacks = null;
1073 }
1074 }
1075
1076 // If the host is unavailable, then we call the associated
1077 // RemoteViewsFactory.onDataSetChanged() directly
1078 if (id.host.callbacks == null) {
1079 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1080 for (FilterComparison key : keys) {
1081 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1082 Intent intent = key.getIntent();
1083
1084 final ServiceConnection conn = new ServiceConnection() {
1085 @Override
1086 public void onServiceConnected(ComponentName name, IBinder service) {
1087 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1088 .asInterface(service);
1089 try {
1090 cb.onDataSetChangedAsync();
1091 } catch (RemoteException e) {
1092 e.printStackTrace();
1093 } catch (RuntimeException e) {
1094 e.printStackTrace();
1095 }
1096 mContext.unbindService(this);
1097 }
1098
1099 @Override
1100 public void onServiceDisconnected(android.content.ComponentName name) {
1101 // Do nothing
1102 }
1103 };
1104
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001105 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001106 // Bind to the service and call onDataSetChanged()
1107 final long token = Binder.clearCallingIdentity();
1108 try {
Amith Yamasani27b89e62013-01-16 12:30:11 -08001109 mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
1110 new UserHandle(userId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001111 } finally {
1112 Binder.restoreCallingIdentity(token);
1113 }
1114 }
1115 }
1116 }
1117 }
1118 }
1119
Adam Cohen3ff2d862012-09-26 14:07:57 -07001120 private boolean isLocalBinder() {
1121 return Process.myPid() == Binder.getCallingPid();
1122 }
1123
1124 private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1125 if (isLocalBinder() && rv != null) {
1126 return rv.clone();
1127 }
1128 return rv;
1129 }
1130
1131 private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
1132 if (isLocalBinder() && info != null) {
1133 return info.clone();
1134 }
1135 return info;
1136 }
1137
1138 private Bundle cloneIfLocalBinder(Bundle bundle) {
1139 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
1140 // if we start adding objects to the options. Further, it would only be an issue if keyguard
1141 // used such options.
1142 if (isLocalBinder() && bundle != null) {
1143 return (Bundle) bundle.clone();
1144 }
1145 return bundle;
1146 }
1147
Amith Yamasani742a6712011-05-04 14:49:28 -07001148 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1149 List<RemoteViews> updatedViews) {
1150 int callingUid = enforceCallingUid(packageName);
1151 synchronized (mAppWidgetIds) {
1152 ensureStateLoadedLocked();
1153 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1154 host.callbacks = callbacks;
1155
1156 updatedViews.clear();
1157
1158 ArrayList<AppWidgetId> instances = host.instances;
1159 int N = instances.size();
1160 int[] updatedIds = new int[N];
1161 for (int i = 0; i < N; i++) {
1162 AppWidgetId id = instances.get(i);
1163 updatedIds[i] = id.appWidgetId;
Adam Cohen3ff2d862012-09-26 14:07:57 -07001164 updatedViews.add(cloneIfLocalBinder(id.views));
Amith Yamasani742a6712011-05-04 14:49:28 -07001165 }
1166 return updatedIds;
1167 }
1168 }
1169
1170 public void stopListening(int hostId) {
1171 synchronized (mAppWidgetIds) {
1172 ensureStateLoadedLocked();
1173 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1174 if (host != null) {
1175 host.callbacks = null;
1176 pruneHostLocked(host);
1177 }
1178 }
1179 }
1180
1181 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
Amith Yamasanic566b432012-11-30 15:26:21 -08001182 if (id.host.uidMatches(callingUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001183 // Apps hosting the AppWidget have access to it.
1184 return true;
1185 }
1186 if (id.provider != null && id.provider.uid == callingUid) {
1187 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1188 return true;
1189 }
1190 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1191 // Apps that can bind have access to all appWidgetIds.
1192 return true;
1193 }
1194 // Nobody else can access it.
1195 return false;
1196 }
1197
1198 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1199 int callingUid = Binder.getCallingUid();
1200 final int N = mAppWidgetIds.size();
1201 for (int i = 0; i < N; i++) {
1202 AppWidgetId id = mAppWidgetIds.get(i);
1203 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1204 return id;
1205 }
1206 }
1207 return null;
1208 }
1209
1210 Provider lookupProviderLocked(ComponentName provider) {
1211 final int N = mInstalledProviders.size();
1212 for (int i = 0; i < N; i++) {
1213 Provider p = mInstalledProviders.get(i);
1214 if (p.info.provider.equals(provider)) {
1215 return p;
1216 }
1217 }
1218 return null;
1219 }
1220
1221 Host lookupHostLocked(int uid, int hostId) {
1222 final int N = mHosts.size();
1223 for (int i = 0; i < N; i++) {
1224 Host h = mHosts.get(i);
Amith Yamasanic566b432012-11-30 15:26:21 -08001225 if (h.uidMatches(uid) && h.hostId == hostId) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001226 return h;
1227 }
1228 }
1229 return null;
1230 }
1231
1232 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1233 final int N = mHosts.size();
1234 for (int i = 0; i < N; i++) {
1235 Host h = mHosts.get(i);
1236 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1237 return h;
1238 }
1239 }
1240 Host host = new Host();
1241 host.packageName = packageName;
1242 host.uid = uid;
1243 host.hostId = hostId;
1244 mHosts.add(host);
1245 return host;
1246 }
1247
1248 void pruneHostLocked(Host host) {
1249 if (host.instances.size() == 0 && host.callbacks == null) {
1250 mHosts.remove(host);
1251 }
1252 }
1253
Adam Cohena1a2f962012-11-01 14:06:16 -07001254 void loadAppWidgetListLocked() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001255 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001256 try {
1257 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1258 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1259 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001260
Amith Yamasani483f3b02012-03-13 16:08:00 -07001261 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1262 for (int i = 0; i < N; i++) {
1263 ResolveInfo ri = broadcastReceivers.get(i);
1264 addProviderLocked(ri);
1265 }
1266 } catch (RemoteException re) {
1267 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001268 }
1269 }
1270
1271 boolean addProviderLocked(ResolveInfo ri) {
1272 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1273 return false;
1274 }
1275 if (!ri.activityInfo.isEnabled()) {
1276 return false;
1277 }
1278 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1279 ri.activityInfo.name), ri);
1280 if (p != null) {
1281 mInstalledProviders.add(p);
1282 return true;
1283 } else {
1284 return false;
1285 }
1286 }
1287
1288 void removeProviderLocked(int index, Provider p) {
1289 int N = p.instances.size();
1290 for (int i = 0; i < N; i++) {
1291 AppWidgetId id = p.instances.get(i);
1292 // Call back with empty RemoteViews
1293 updateAppWidgetInstanceLocked(id, null);
1294 // Stop telling the host about updates for this from now on
1295 cancelBroadcasts(p);
1296 // clear out references to this appWidgetId
1297 id.host.instances.remove(id);
1298 mAppWidgetIds.remove(id);
1299 id.provider = null;
1300 pruneHostLocked(id.host);
1301 id.host = null;
1302 }
1303 p.instances.clear();
1304 mInstalledProviders.remove(index);
1305 mDeletedProviders.add(p);
1306 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1307 cancelBroadcasts(p);
1308 }
1309
1310 void sendEnableIntentLocked(Provider p) {
1311 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1312 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001313 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001314 }
1315
1316 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1317 if (appWidgetIds != null && appWidgetIds.length > 0) {
1318 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1319 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1320 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001321 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001322 }
1323 }
1324
1325 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1326 if (p.info.updatePeriodMillis > 0) {
1327 // if this is the first instance, set the alarm. otherwise,
1328 // rely on the fact that we've already set it and that
1329 // PendingIntent.getBroadcast will update the extras.
1330 boolean alreadyRegistered = p.broadcast != null;
1331 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1332 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1333 intent.setComponent(p.info.provider);
1334 long token = Binder.clearCallingIdentity();
1335 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001336 p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
1337 PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001338 } finally {
1339 Binder.restoreCallingIdentity(token);
1340 }
1341 if (!alreadyRegistered) {
1342 long period = p.info.updatePeriodMillis;
1343 if (period < MIN_UPDATE_PERIOD) {
1344 period = MIN_UPDATE_PERIOD;
1345 }
1346 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1347 .elapsedRealtime()
1348 + period, period, p.broadcast);
1349 }
1350 }
1351 }
1352
1353 static int[] getAppWidgetIds(Provider p) {
1354 int instancesSize = p.instances.size();
1355 int appWidgetIds[] = new int[instancesSize];
1356 for (int i = 0; i < instancesSize; i++) {
1357 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1358 }
1359 return appWidgetIds;
1360 }
1361
1362 public int[] getAppWidgetIds(ComponentName provider) {
1363 synchronized (mAppWidgetIds) {
1364 ensureStateLoadedLocked();
1365 Provider p = lookupProviderLocked(provider);
1366 if (p != null && Binder.getCallingUid() == p.uid) {
1367 return getAppWidgetIds(p);
1368 } else {
1369 return new int[0];
1370 }
1371 }
1372 }
1373
Michael Jurka75b5cfb2012-11-15 18:22:47 -08001374 static int[] getAppWidgetIds(Host h) {
1375 int instancesSize = h.instances.size();
1376 int appWidgetIds[] = new int[instancesSize];
1377 for (int i = 0; i < instancesSize; i++) {
1378 appWidgetIds[i] = h.instances.get(i).appWidgetId;
1379 }
1380 return appWidgetIds;
1381 }
1382
1383 public int[] getAppWidgetIdsForHost(int hostId) {
1384 synchronized (mAppWidgetIds) {
1385 ensureStateLoadedLocked();
1386 int callingUid = Binder.getCallingUid();
1387 Host host = lookupHostLocked(callingUid, hostId);
1388 if (host != null) {
1389 return getAppWidgetIds(host);
1390 } else {
1391 return new int[0];
1392 }
1393 }
1394 }
1395
Amith Yamasani742a6712011-05-04 14:49:28 -07001396 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1397 Provider p = null;
1398
1399 ActivityInfo activityInfo = ri.activityInfo;
1400 XmlResourceParser parser = null;
1401 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001402 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001403 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1404 if (parser == null) {
1405 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1406 + " meta-data for " + "AppWidget provider '" + component + '\'');
1407 return null;
1408 }
1409
1410 AttributeSet attrs = Xml.asAttributeSet(parser);
1411
1412 int type;
1413 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1414 && type != XmlPullParser.START_TAG) {
1415 // drain whitespace, comments, etc.
1416 }
1417
1418 String nodeName = parser.getName();
1419 if (!"appwidget-provider".equals(nodeName)) {
1420 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1421 + " AppWidget provider '" + component + '\'');
1422 return null;
1423 }
1424
1425 p = new Provider();
1426 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1427 info.provider = component;
1428 p.uid = activityInfo.applicationInfo.uid;
1429
Amith Yamasani483f3b02012-03-13 16:08:00 -07001430 Resources res = mContext.getPackageManager()
Amith Yamasani8320de82012-10-05 16:10:38 -07001431 .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001432
1433 TypedArray sa = res.obtainAttributes(attrs,
1434 com.android.internal.R.styleable.AppWidgetProviderInfo);
1435
1436 // These dimensions has to be resolved in the application's context.
1437 // We simply send back the raw complex data, which will be
1438 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1439 TypedValue value = sa
1440 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1441 info.minWidth = value != null ? value.data : 0;
1442 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1443 info.minHeight = value != null ? value.data : 0;
1444 value = sa.peekValue(
1445 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1446 info.minResizeWidth = value != null ? value.data : info.minWidth;
1447 value = sa.peekValue(
1448 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1449 info.minResizeHeight = value != null ? value.data : info.minHeight;
1450 info.updatePeriodMillis = sa.getInt(
1451 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1452 info.initialLayout = sa.getResourceId(
1453 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001454 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1455 AppWidgetProviderInfo_initialKeyguardLayout, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001456 String className = sa
1457 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1458 if (className != null) {
1459 info.configure = new ComponentName(component.getPackageName(), className);
1460 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001461 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001462 info.icon = ri.getIconResource();
1463 info.previewImage = sa.getResourceId(
1464 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1465 info.autoAdvanceViewId = sa.getResourceId(
1466 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1467 info.resizeMode = sa.getInt(
1468 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1469 AppWidgetProviderInfo.RESIZE_NONE);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001470 info.widgetCategory = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001471 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001472 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
Amith Yamasani742a6712011-05-04 14:49:28 -07001473
1474 sa.recycle();
1475 } catch (Exception e) {
1476 // Ok to catch Exception here, because anything going wrong because
1477 // of what a client process passes to us should not be fatal for the
1478 // system process.
1479 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1480 return null;
1481 } finally {
1482 if (parser != null)
1483 parser.close();
1484 }
1485 return p;
1486 }
1487
1488 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001489 PackageInfo pkgInfo = null;
1490 try {
1491 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1492 } catch (RemoteException re) {
1493 // Shouldn't happen, local call
1494 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001495 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1496 throw new PackageManager.NameNotFoundException();
1497 }
1498 return pkgInfo.applicationInfo.uid;
1499 }
1500
Jim Millerf229e4d2012-09-12 20:32:50 -07001501 int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1502 int callingUid = Binder.getCallingUid();
Michael Jurka03bdc8a2012-09-21 16:10:21 -07001503 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
Jim Millerf229e4d2012-09-12 20:32:50 -07001504 return callingUid;
1505 }
1506 return enforceCallingUid(packageName);
1507 }
1508
Amith Yamasani742a6712011-05-04 14:49:28 -07001509 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1510 int callingUid = Binder.getCallingUid();
1511 int packageUid;
1512 try {
1513 packageUid = getUidForPackage(packageName);
1514 } catch (PackageManager.NameNotFoundException ex) {
1515 throw new IllegalArgumentException("packageName and uid don't match packageName="
1516 + packageName);
1517 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001518 if (!UserHandle.isSameApp(callingUid, packageUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001519 throw new IllegalArgumentException("packageName and uid don't match packageName="
1520 + packageName);
1521 }
1522 return callingUid;
1523 }
1524
1525 void sendInitialBroadcasts() {
1526 synchronized (mAppWidgetIds) {
1527 ensureStateLoadedLocked();
1528 final int N = mInstalledProviders.size();
1529 for (int i = 0; i < N; i++) {
1530 Provider p = mInstalledProviders.get(i);
1531 if (p.instances.size() > 0) {
1532 sendEnableIntentLocked(p);
1533 int[] appWidgetIds = getAppWidgetIds(p);
1534 sendUpdateIntentLocked(p, appWidgetIds);
1535 registerForBroadcastsLocked(p, appWidgetIds);
1536 }
1537 }
1538 }
1539 }
1540
1541 // only call from initialization -- it assumes that the data structures are all empty
1542 void loadStateLocked() {
1543 AtomicFile file = savedStateFile();
1544 try {
1545 FileInputStream stream = file.openRead();
1546 readStateFromFileLocked(stream);
1547
1548 if (stream != null) {
1549 try {
1550 stream.close();
1551 } catch (IOException e) {
1552 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1553 }
1554 }
1555 } catch (FileNotFoundException e) {
1556 Slog.w(TAG, "Failed to read state: " + e);
1557 }
1558 }
1559
1560 void saveStateLocked() {
1561 AtomicFile file = savedStateFile();
1562 FileOutputStream stream;
1563 try {
1564 stream = file.startWrite();
1565 if (writeStateToFileLocked(stream)) {
1566 file.finishWrite(stream);
1567 } else {
1568 file.failWrite(stream);
1569 Slog.w(TAG, "Failed to save state, restoring backup.");
1570 }
1571 } catch (IOException e) {
1572 Slog.w(TAG, "Failed open state file for write: " + e);
1573 }
1574 }
1575
1576 boolean writeStateToFileLocked(FileOutputStream stream) {
1577 int N;
1578
1579 try {
1580 XmlSerializer out = new FastXmlSerializer();
1581 out.setOutput(stream, "utf-8");
1582 out.startDocument(null, true);
1583 out.startTag(null, "gs");
1584
1585 int providerIndex = 0;
1586 N = mInstalledProviders.size();
1587 for (int i = 0; i < N; i++) {
1588 Provider p = mInstalledProviders.get(i);
1589 if (p.instances.size() > 0) {
1590 out.startTag(null, "p");
1591 out.attribute(null, "pkg", p.info.provider.getPackageName());
1592 out.attribute(null, "cl", p.info.provider.getClassName());
1593 out.endTag(null, "p");
1594 p.tag = providerIndex;
1595 providerIndex++;
1596 }
1597 }
1598
1599 N = mHosts.size();
1600 for (int i = 0; i < N; i++) {
1601 Host host = mHosts.get(i);
1602 out.startTag(null, "h");
1603 out.attribute(null, "pkg", host.packageName);
1604 out.attribute(null, "id", Integer.toHexString(host.hostId));
1605 out.endTag(null, "h");
1606 host.tag = i;
1607 }
1608
1609 N = mAppWidgetIds.size();
1610 for (int i = 0; i < N; i++) {
1611 AppWidgetId id = mAppWidgetIds.get(i);
1612 out.startTag(null, "g");
1613 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1614 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1615 if (id.provider != null) {
1616 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1617 }
Adam Cohen0aa2d422012-09-07 17:37:26 -07001618 if (id.options != null) {
1619 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1620 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1621 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1622 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1623 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1624 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1625 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1626 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1627 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1628 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1629 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001630 out.endTag(null, "g");
1631 }
1632
Michael Jurka61a5b012012-04-13 10:39:45 -07001633 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1634 while (it.hasNext()) {
1635 out.startTag(null, "b");
1636 out.attribute(null, "packageName", it.next());
1637 out.endTag(null, "b");
1638 }
1639
Amith Yamasani742a6712011-05-04 14:49:28 -07001640 out.endTag(null, "gs");
1641
1642 out.endDocument();
1643 return true;
1644 } catch (IOException e) {
1645 Slog.w(TAG, "Failed to write state: " + e);
1646 return false;
1647 }
1648 }
1649
Adam Cohen0aa2d422012-09-07 17:37:26 -07001650 @SuppressWarnings("unused")
Amith Yamasani742a6712011-05-04 14:49:28 -07001651 void readStateFromFileLocked(FileInputStream stream) {
1652 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001653 try {
1654 XmlPullParser parser = Xml.newPullParser();
1655 parser.setInput(stream, null);
1656
1657 int type;
1658 int providerIndex = 0;
1659 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1660 do {
1661 type = parser.next();
1662 if (type == XmlPullParser.START_TAG) {
1663 String tag = parser.getName();
1664 if ("p".equals(tag)) {
1665 // TODO: do we need to check that this package has the same signature
1666 // as before?
1667 String pkg = parser.getAttributeValue(null, "pkg");
1668 String cl = parser.getAttributeValue(null, "cl");
1669
Amith Yamasanif203aee2012-08-29 18:41:53 -07001670 final IPackageManager packageManager = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -07001671 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001672 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
Amith Yamasanif203aee2012-08-29 18:41:53 -07001673 } catch (RemoteException e) {
1674 String[] pkgs = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001675 .currentToCanonicalPackageNames(new String[] { pkg });
1676 pkg = pkgs[0];
1677 }
1678
1679 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1680 if (p == null && mSafeMode) {
1681 // if we're in safe mode, make a temporary one
1682 p = new Provider();
1683 p.info = new AppWidgetProviderInfo();
1684 p.info.provider = new ComponentName(pkg, cl);
1685 p.zombie = true;
1686 mInstalledProviders.add(p);
1687 }
1688 if (p != null) {
1689 // if it wasn't uninstalled or something
1690 loadedProviders.put(providerIndex, p);
1691 }
1692 providerIndex++;
1693 } else if ("h".equals(tag)) {
1694 Host host = new Host();
1695
1696 // TODO: do we need to check that this package has the same signature
1697 // as before?
1698 host.packageName = parser.getAttributeValue(null, "pkg");
1699 try {
1700 host.uid = getUidForPackage(host.packageName);
1701 } catch (PackageManager.NameNotFoundException ex) {
1702 host.zombie = true;
1703 }
1704 if (!host.zombie || mSafeMode) {
1705 // In safe mode, we don't discard the hosts we don't recognize
1706 // so that they're not pruned from our list. Otherwise, we do.
1707 host.hostId = Integer
1708 .parseInt(parser.getAttributeValue(null, "id"), 16);
1709 mHosts.add(host);
1710 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001711 } else if ("b".equals(tag)) {
1712 String packageName = parser.getAttributeValue(null, "packageName");
1713 if (packageName != null) {
1714 mPackagesWithBindWidgetPermission.add(packageName);
1715 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001716 } else if ("g".equals(tag)) {
1717 AppWidgetId id = new AppWidgetId();
1718 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1719 if (id.appWidgetId >= mNextAppWidgetId) {
1720 mNextAppWidgetId = id.appWidgetId + 1;
1721 }
1722
Adam Cohen0aa2d422012-09-07 17:37:26 -07001723 Bundle options = new Bundle();
1724 String minWidthString = parser.getAttributeValue(null, "min_width");
1725 if (minWidthString != null) {
1726 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1727 Integer.parseInt(minWidthString, 16));
1728 }
1729 String minHeightString = parser.getAttributeValue(null, "min_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001730 if (minHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001731 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1732 Integer.parseInt(minHeightString, 16));
1733 }
Adam Cohendb38d8a2012-09-21 18:14:58 -07001734 String maxWidthString = parser.getAttributeValue(null, "max_width");
1735 if (maxWidthString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001736 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1737 Integer.parseInt(maxWidthString, 16));
1738 }
1739 String maxHeightString = parser.getAttributeValue(null, "max_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001740 if (maxHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001741 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1742 Integer.parseInt(maxHeightString, 16));
1743 }
1744 String categoryString = parser.getAttributeValue(null, "host_category");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001745 if (categoryString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001746 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1747 Integer.parseInt(categoryString, 16));
1748 }
1749 id.options = options;
1750
Amith Yamasani742a6712011-05-04 14:49:28 -07001751 String providerString = parser.getAttributeValue(null, "p");
1752 if (providerString != null) {
1753 // there's no provider if it hasn't been bound yet.
1754 // maybe we don't have to save this, but it brings the system
1755 // to the state it was in.
1756 int pIndex = Integer.parseInt(providerString, 16);
1757 id.provider = loadedProviders.get(pIndex);
1758 if (false) {
1759 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1760 + pIndex + " which is " + id.provider);
1761 }
1762 if (id.provider == null) {
1763 // This provider is gone. We just let the host figure out
1764 // that this happened when it fails to load it.
1765 continue;
1766 }
1767 }
1768
1769 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1770 id.host = mHosts.get(hIndex);
1771 if (id.host == null) {
1772 // This host is gone.
1773 continue;
1774 }
1775
1776 if (id.provider != null) {
1777 id.provider.instances.add(id);
1778 }
1779 id.host.instances.add(id);
1780 mAppWidgetIds.add(id);
1781 }
1782 }
1783 } while (type != XmlPullParser.END_DOCUMENT);
1784 success = true;
1785 } catch (NullPointerException e) {
1786 Slog.w(TAG, "failed parsing " + e);
1787 } catch (NumberFormatException e) {
1788 Slog.w(TAG, "failed parsing " + e);
1789 } catch (XmlPullParserException e) {
1790 Slog.w(TAG, "failed parsing " + e);
1791 } catch (IOException e) {
1792 Slog.w(TAG, "failed parsing " + e);
1793 } catch (IndexOutOfBoundsException e) {
1794 Slog.w(TAG, "failed parsing " + e);
1795 }
1796
1797 if (success) {
1798 // delete any hosts that didn't manage to get connected (should happen)
1799 // if it matters, they'll be reconnected.
1800 for (int i = mHosts.size() - 1; i >= 0; i--) {
1801 pruneHostLocked(mHosts.get(i));
1802 }
1803 } else {
1804 // failed reading, clean up
1805 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1806
1807 mAppWidgetIds.clear();
1808 mHosts.clear();
1809 final int N = mInstalledProviders.size();
1810 for (int i = 0; i < N; i++) {
1811 mInstalledProviders.get(i).instances.clear();
1812 }
1813 }
1814 }
1815
Amith Yamasani13593602012-03-22 16:16:17 -07001816 static File getSettingsFile(int userId) {
Amith Yamasani61f57372012-08-31 12:12:28 -07001817 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
Amith Yamasani13593602012-03-22 16:16:17 -07001818 }
1819
Amith Yamasani742a6712011-05-04 14:49:28 -07001820 AtomicFile savedStateFile() {
Amith Yamasani61f57372012-08-31 12:12:28 -07001821 File dir = Environment.getUserSystemDirectory(mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001822 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001823 if (!settingsFile.exists() && mUserId == 0) {
1824 if (!dir.exists()) {
1825 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001826 }
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001827 // Migrate old data
1828 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1829 // Method doesn't throw an exception on failure. Ignore any errors
1830 // in moving the file (like non-existence)
1831 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001832 }
1833 return new AtomicFile(settingsFile);
1834 }
1835
Amith Yamasani756901d2012-10-12 12:30:07 -07001836 void onUserStopping() {
Amith Yamasani13593602012-03-22 16:16:17 -07001837 // prune the ones we don't want to keep
1838 int N = mInstalledProviders.size();
1839 for (int i = N - 1; i >= 0; i--) {
1840 Provider p = mInstalledProviders.get(i);
1841 cancelBroadcasts(p);
1842 }
Amith Yamasani756901d2012-10-12 12:30:07 -07001843 }
1844
1845 void onUserRemoved() {
Amith Yamasani13593602012-03-22 16:16:17 -07001846 getSettingsFile(mUserId).delete();
1847 }
1848
Winson Chung7fbd2842012-06-13 10:35:51 -07001849 boolean addProvidersForPackageLocked(String pkgName) {
1850 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001851 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1852 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001853 List<ResolveInfo> broadcastReceivers;
1854 try {
1855 broadcastReceivers = mPm.queryIntentReceivers(intent,
1856 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1857 PackageManager.GET_META_DATA, mUserId);
1858 } catch (RemoteException re) {
1859 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001860 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001861 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001862 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1863 for (int i = 0; i < N; i++) {
1864 ResolveInfo ri = broadcastReceivers.get(i);
1865 ActivityInfo ai = ri.activityInfo;
1866 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1867 continue;
1868 }
1869 if (pkgName.equals(ai.packageName)) {
1870 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001871 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001872 }
1873 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001874
1875 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001876 }
1877
Winson Chunga3195052012-06-25 10:02:10 -07001878 /**
1879 * Updates all providers with the specified package names, and records any providers that were
1880 * pruned.
1881 *
1882 * @return whether any providers were updated
1883 */
1884 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001885 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001886 HashSet<String> keep = new HashSet<String>();
1887 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1888 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001889 List<ResolveInfo> broadcastReceivers;
1890 try {
1891 broadcastReceivers = mPm.queryIntentReceivers(intent,
1892 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1893 PackageManager.GET_META_DATA, mUserId);
1894 } catch (RemoteException re) {
1895 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001896 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001897 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001898
1899 // add the missing ones and collect which ones to keep
1900 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1901 for (int i = 0; i < N; i++) {
1902 ResolveInfo ri = broadcastReceivers.get(i);
1903 ActivityInfo ai = ri.activityInfo;
1904 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1905 continue;
1906 }
1907 if (pkgName.equals(ai.packageName)) {
1908 ComponentName component = new ComponentName(ai.packageName, ai.name);
1909 Provider p = lookupProviderLocked(component);
1910 if (p == null) {
1911 if (addProviderLocked(ri)) {
1912 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001913 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001914 }
1915 } else {
1916 Provider parsed = parseProviderInfoXml(component, ri);
1917 if (parsed != null) {
1918 keep.add(ai.name);
1919 // Use the new AppWidgetProviderInfo.
1920 p.info = parsed.info;
1921 // If it's enabled
1922 final int M = p.instances.size();
1923 if (M > 0) {
1924 int[] appWidgetIds = getAppWidgetIds(p);
1925 // Reschedule for the new updatePeriodMillis (don't worry about handling
1926 // it specially if updatePeriodMillis didn't change because we just sent
1927 // an update, and the next one will be updatePeriodMillis from now).
1928 cancelBroadcasts(p);
1929 registerForBroadcastsLocked(p, appWidgetIds);
1930 // If it's currently showing, call back with the new
1931 // AppWidgetProviderInfo.
1932 for (int j = 0; j < M; j++) {
1933 AppWidgetId id = p.instances.get(j);
1934 id.views = null;
1935 if (id.host != null && id.host.callbacks != null) {
1936 try {
1937 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1938 } catch (RemoteException ex) {
1939 // It failed; remove the callback. No need to prune because
1940 // we know that this host is still referenced by this
1941 // instance.
1942 id.host.callbacks = null;
1943 }
1944 }
1945 }
1946 // Now that we've told the host, push out an update.
1947 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07001948 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001949 }
1950 }
1951 }
1952 }
1953 }
1954
1955 // prune the ones we don't want to keep
1956 N = mInstalledProviders.size();
1957 for (int i = N - 1; i >= 0; i--) {
1958 Provider p = mInstalledProviders.get(i);
1959 if (pkgName.equals(p.info.provider.getPackageName())
1960 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07001961 if (removedProviders != null) {
1962 removedProviders.add(p.info.provider);
1963 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001964 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001965 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001966 }
1967 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001968
1969 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07001970 }
1971
Winson Chung7fbd2842012-06-13 10:35:51 -07001972 boolean removeProvidersForPackageLocked(String pkgName) {
1973 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001974 int N = mInstalledProviders.size();
1975 for (int i = N - 1; i >= 0; i--) {
1976 Provider p = mInstalledProviders.get(i);
1977 if (pkgName.equals(p.info.provider.getPackageName())) {
1978 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001979 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001980 }
1981 }
1982
1983 // Delete the hosts for this package too
1984 //
1985 // By now, we have removed any AppWidgets that were in any hosts here,
1986 // so we don't need to worry about sending DISABLE broadcasts to them.
1987 N = mHosts.size();
1988 for (int i = N - 1; i >= 0; i--) {
1989 Host host = mHosts.get(i);
1990 if (pkgName.equals(host.packageName)) {
1991 deleteHostLocked(host);
1992 }
1993 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001994
1995 return providersRemoved;
1996 }
1997
1998 void notifyHostsForProvidersChangedLocked() {
1999 final int N = mHosts.size();
2000 for (int i = N - 1; i >= 0; i--) {
2001 Host host = mHosts.get(i);
2002 try {
2003 if (host.callbacks != null) {
2004 host.callbacks.providersChanged();
2005 }
2006 } catch (RemoteException ex) {
2007 // It failed; remove the callback. No need to prune because
2008 // we know that this host is still referenced by this
2009 // instance.
2010 host.callbacks = null;
2011 }
2012 }
Amith Yamasani742a6712011-05-04 14:49:28 -07002013 }
2014}