blob: bba5192d9440f395c5f6865c2cd28e4e5d301ea5 [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 Millerf229e4d32012-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)
119 }
120
121 static class AppWidgetId {
122 int appWidgetId;
123 Provider provider;
124 RemoteViews views;
Adam Cohend2097eb2012-05-01 18:10:28 -0700125 Bundle options;
Amith Yamasani742a6712011-05-04 14:49:28 -0700126 Host host;
127 }
128
129 /**
130 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
131 * needs to be a static inner class since a reference to the ServiceConnection is held globally
132 * and may lead us to leak AppWidgetService instances (if there were more than one).
133 */
134 static class ServiceConnectionProxy implements ServiceConnection {
135 private final IBinder mConnectionCb;
136
137 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
138 mConnectionCb = connectionCb;
139 }
140
141 public void onServiceConnected(ComponentName name, IBinder service) {
142 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
143 .asInterface(mConnectionCb);
144 try {
145 cb.onServiceConnected(service);
146 } catch (Exception e) {
147 e.printStackTrace();
148 }
149 }
150
151 public void onServiceDisconnected(ComponentName name) {
152 disconnect();
153 }
154
155 public void disconnect() {
156 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
157 .asInterface(mConnectionCb);
158 try {
159 cb.onServiceDisconnected();
160 } catch (Exception e) {
161 e.printStackTrace();
162 }
163 }
164 }
165
166 // Manages active connections to RemoteViewsServices
167 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
168 // Manages persistent references to RemoteViewsServices from different App Widgets
169 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
170
171 Context mContext;
172 Locale mLocale;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700173 IPackageManager mPm;
Amith Yamasani742a6712011-05-04 14:49:28 -0700174 AlarmManager mAlarmManager;
175 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
176 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
177 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
178 ArrayList<Host> mHosts = new ArrayList<Host>();
Michael Jurka61a5b012012-04-13 10:39:45 -0700179 // set of package names
180 HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
Amith Yamasani742a6712011-05-04 14:49:28 -0700181 boolean mSafeMode;
182 int mUserId;
183 boolean mStateLoaded;
Adam Cohen311c79c2012-05-10 14:44:38 -0700184 int mMaxWidgetBitmapMemory;
Amith Yamasani742a6712011-05-04 14:49:28 -0700185
Adam Cohena1a2f962012-11-01 14:06:16 -0700186 private final Handler mSaveStateHandler;
187
Amith Yamasani742a6712011-05-04 14:49:28 -0700188 // These are for debugging only -- widgets are going missing in some rare instances
189 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
190 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
191
Adam Cohena1a2f962012-11-01 14:06:16 -0700192 AppWidgetServiceImpl(Context context, int userId, Handler saveStateHandler) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700193 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700194 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700195 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
196 mUserId = userId;
Adam Cohena1a2f962012-11-01 14:06:16 -0700197 mSaveStateHandler = saveStateHandler;
Adam Cohen311c79c2012-05-10 14:44:38 -0700198 computeMaximumWidgetBitmapMemory();
199 }
200
201 void computeMaximumWidgetBitmapMemory() {
202 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Jeff Browna8b9def2012-07-23 14:22:49 -0700203 Display display = wm.getDefaultDisplay();
204 Point size = new Point();
205 display.getRealSize(size);
Winson Chunge92aad42012-06-22 14:11:47 -0700206 // Cap memory usage at 1.5 times the size of the display
207 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
Jeff Browna8b9def2012-07-23 14:22:49 -0700208 mMaxWidgetBitmapMemory = 6 * size.x * size.y;
Amith Yamasani742a6712011-05-04 14:49:28 -0700209 }
210
211 public void systemReady(boolean safeMode) {
212 mSafeMode = safeMode;
213
214 synchronized (mAppWidgetIds) {
215 ensureStateLoadedLocked();
216 }
217 }
218
Amith Yamasani8320de82012-10-05 16:10:38 -0700219 private void log(String msg) {
220 Slog.i(TAG, "u=" + mUserId + ": " + msg);
221 }
222
Amith Yamasani742a6712011-05-04 14:49:28 -0700223 void onConfigurationChanged() {
Amith Yamasani8320de82012-10-05 16:10:38 -0700224 if (DBG) log("Got onConfigurationChanged()");
Amith Yamasani742a6712011-05-04 14:49:28 -0700225 Locale revised = Locale.getDefault();
226 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
227 mLocale = revised;
228
229 synchronized (mAppWidgetIds) {
230 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700231 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
232 // list of installed providers and skip providers that we don't need to update.
233 // Also note that remove the provider does not clear the Provider component data.
234 ArrayList<Provider> installedProviders =
235 new ArrayList<Provider>(mInstalledProviders);
236 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
237 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700238 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700239 Provider p = installedProviders.get(i);
240 ComponentName cn = p.info.provider;
241 if (!removedProviders.contains(cn)) {
242 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
243 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700244 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700245 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700246 }
247 }
248 }
249
250 void onBroadcastReceived(Intent intent) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700251 if (DBG) log("onBroadcast " + intent);
Amith Yamasani742a6712011-05-04 14:49:28 -0700252 final String action = intent.getAction();
253 boolean added = false;
254 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700255 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700256 String pkgList[] = null;
257 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
258 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
259 added = true;
260 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
261 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
262 added = false;
263 } else {
264 Uri uri = intent.getData();
265 if (uri == null) {
266 return;
267 }
268 String pkgName = uri.getSchemeSpecificPart();
269 if (pkgName == null) {
270 return;
271 }
272 pkgList = new String[] { pkgName };
273 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
274 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
275 }
276 if (pkgList == null || pkgList.length == 0) {
277 return;
278 }
279 if (added || changed) {
280 synchronized (mAppWidgetIds) {
281 ensureStateLoadedLocked();
282 Bundle extras = intent.getExtras();
283 if (changed
284 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
285 for (String pkgName : pkgList) {
286 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700287 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700288 }
289 } else {
290 // The package was just added
291 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700292 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700293 }
294 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700295 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700296 }
297 } else {
298 Bundle extras = intent.getExtras();
299 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
300 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
301 } else {
302 synchronized (mAppWidgetIds) {
303 ensureStateLoadedLocked();
304 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700305 providersModified |= removeProvidersForPackageLocked(pkgName);
Adam Cohena1a2f962012-11-01 14:06:16 -0700306 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700307 }
308 }
309 }
310 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700311
312 if (providersModified) {
313 // If the set of providers has been modified, notify each active AppWidgetHost
314 synchronized (mAppWidgetIds) {
315 ensureStateLoadedLocked();
316 notifyHostsForProvidersChangedLocked();
317 }
318 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700319 }
320
321 private void dumpProvider(Provider p, int index, PrintWriter pw) {
322 AppWidgetProviderInfo info = p.info;
323 pw.print(" ["); pw.print(index); pw.print("] provider ");
324 pw.print(info.provider.flattenToShortString());
325 pw.println(':');
326 pw.print(" min=("); pw.print(info.minWidth);
327 pw.print("x"); pw.print(info.minHeight);
328 pw.print(") minResize=("); pw.print(info.minResizeWidth);
329 pw.print("x"); pw.print(info.minResizeHeight);
330 pw.print(") updatePeriodMillis=");
331 pw.print(info.updatePeriodMillis);
332 pw.print(" resizeMode=");
333 pw.print(info.resizeMode);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700334 pw.print(info.widgetCategory);
Amith Yamasani742a6712011-05-04 14:49:28 -0700335 pw.print(" autoAdvanceViewId=");
336 pw.print(info.autoAdvanceViewId);
337 pw.print(" initialLayout=#");
338 pw.print(Integer.toHexString(info.initialLayout));
Amith Yamasani791f8772012-11-21 14:06:07 -0800339 pw.print(" uid="); pw.print(p.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700340 pw.print(" zombie="); pw.println(p.zombie);
341 }
342
343 private void dumpHost(Host host, int index, PrintWriter pw) {
344 pw.print(" ["); pw.print(index); pw.print("] hostId=");
345 pw.print(host.hostId); pw.print(' ');
346 pw.print(host.packageName); pw.print('/');
347 pw.print(host.uid); pw.println(':');
348 pw.print(" callbacks="); pw.println(host.callbacks);
349 pw.print(" instances.size="); pw.print(host.instances.size());
350 pw.print(" zombie="); pw.println(host.zombie);
351 }
352
353 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
354 pw.print(" ["); pw.print(index); pw.print("] id=");
355 pw.println(id.appWidgetId);
356 pw.print(" hostId=");
357 pw.print(id.host.hostId); pw.print(' ');
358 pw.print(id.host.packageName); pw.print('/');
359 pw.println(id.host.uid);
360 if (id.provider != null) {
361 pw.print(" provider=");
362 pw.println(id.provider.info.provider.flattenToShortString());
363 }
364 if (id.host != null) {
365 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
366 }
367 if (id.views != null) {
368 pw.print(" views="); pw.println(id.views);
369 }
370 }
371
372 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
373 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
374 != PackageManager.PERMISSION_GRANTED) {
375 pw.println("Permission Denial: can't dump from from pid="
376 + Binder.getCallingPid()
377 + ", uid=" + Binder.getCallingUid());
378 return;
379 }
380
381 synchronized (mAppWidgetIds) {
382 int N = mInstalledProviders.size();
383 pw.println("Providers:");
384 for (int i=0; i<N; i++) {
385 dumpProvider(mInstalledProviders.get(i), i, pw);
386 }
387
388 N = mAppWidgetIds.size();
389 pw.println(" ");
390 pw.println("AppWidgetIds:");
391 for (int i=0; i<N; i++) {
392 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
393 }
394
395 N = mHosts.size();
396 pw.println(" ");
397 pw.println("Hosts:");
398 for (int i=0; i<N; i++) {
399 dumpHost(mHosts.get(i), i, pw);
400 }
401
402 N = mDeletedProviders.size();
403 pw.println(" ");
404 pw.println("Deleted Providers:");
405 for (int i=0; i<N; i++) {
406 dumpProvider(mDeletedProviders.get(i), i, pw);
407 }
408
409 N = mDeletedHosts.size();
410 pw.println(" ");
411 pw.println("Deleted Hosts:");
412 for (int i=0; i<N; i++) {
413 dumpHost(mDeletedHosts.get(i), i, pw);
414 }
415 }
416 }
417
418 private void ensureStateLoadedLocked() {
419 if (!mStateLoaded) {
Adam Cohena1a2f962012-11-01 14:06:16 -0700420 loadAppWidgetListLocked();
Amith Yamasani742a6712011-05-04 14:49:28 -0700421 loadStateLocked();
422 mStateLoaded = true;
423 }
424 }
425
426 public int allocateAppWidgetId(String packageName, int hostId) {
Jim Millerf229e4d32012-09-12 20:32:50 -0700427 int callingUid = enforceSystemOrCallingUid(packageName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700428 synchronized (mAppWidgetIds) {
429 ensureStateLoadedLocked();
430 int appWidgetId = mNextAppWidgetId++;
431
432 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
433
434 AppWidgetId id = new AppWidgetId();
435 id.appWidgetId = appWidgetId;
436 id.host = host;
437
438 host.instances.add(id);
439 mAppWidgetIds.add(id);
440
Adam Cohena1a2f962012-11-01 14:06:16 -0700441 saveStateAsync();
Amith Yamasani8320de82012-10-05 16:10:38 -0700442 if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
443 + " id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700444 return appWidgetId;
445 }
446 }
447
448 public void deleteAppWidgetId(int appWidgetId) {
449 synchronized (mAppWidgetIds) {
450 ensureStateLoadedLocked();
451 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
452 if (id != null) {
453 deleteAppWidgetLocked(id);
Adam Cohena1a2f962012-11-01 14:06:16 -0700454 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700455 }
456 }
457 }
458
459 public void deleteHost(int hostId) {
460 synchronized (mAppWidgetIds) {
461 ensureStateLoadedLocked();
462 int callingUid = Binder.getCallingUid();
463 Host host = lookupHostLocked(callingUid, hostId);
464 if (host != null) {
465 deleteHostLocked(host);
Adam Cohena1a2f962012-11-01 14:06:16 -0700466 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700467 }
468 }
469 }
470
471 public void deleteAllHosts() {
472 synchronized (mAppWidgetIds) {
473 ensureStateLoadedLocked();
474 int callingUid = Binder.getCallingUid();
475 final int N = mHosts.size();
476 boolean changed = false;
477 for (int i = N - 1; i >= 0; i--) {
478 Host host = mHosts.get(i);
479 if (host.uid == callingUid) {
480 deleteHostLocked(host);
481 changed = true;
482 }
483 }
484 if (changed) {
Adam Cohena1a2f962012-11-01 14:06:16 -0700485 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700486 }
487 }
488 }
489
490 void deleteHostLocked(Host host) {
491 final int N = host.instances.size();
492 for (int i = N - 1; i >= 0; i--) {
493 AppWidgetId id = host.instances.get(i);
494 deleteAppWidgetLocked(id);
495 }
496 host.instances.clear();
497 mHosts.remove(host);
498 mDeletedHosts.add(host);
499 // it's gone or going away, abruptly drop the callback connection
500 host.callbacks = null;
501 }
502
503 void deleteAppWidgetLocked(AppWidgetId id) {
504 // We first unbind all services that are bound to this id
505 unbindAppWidgetRemoteViewsServicesLocked(id);
506
507 Host host = id.host;
508 host.instances.remove(id);
509 pruneHostLocked(host);
510
511 mAppWidgetIds.remove(id);
512
513 Provider p = id.provider;
514 if (p != null) {
515 p.instances.remove(id);
516 if (!p.zombie) {
517 // send the broacast saying that this appWidgetId has been deleted
518 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
519 intent.setComponent(p.info.provider);
520 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700521 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700522 if (p.instances.size() == 0) {
523 // cancel the future updates
524 cancelBroadcasts(p);
525
526 // send the broacast saying that the provider is not in use any more
527 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
528 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700529 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700530 }
531 }
532 }
533 }
534
535 void cancelBroadcasts(Provider p) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700536 if (DBG) log("cancelBroadcasts for " + p);
Amith Yamasani742a6712011-05-04 14:49:28 -0700537 if (p.broadcast != null) {
538 mAlarmManager.cancel(p.broadcast);
539 long token = Binder.clearCallingIdentity();
540 try {
541 p.broadcast.cancel();
542 } finally {
543 Binder.restoreCallingIdentity(token);
544 }
545 p.broadcast = null;
546 }
547 }
548
Adam Cohen0aa2d422012-09-07 17:37:26 -0700549 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700550 if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
551 + " provider=" + provider);
Amith Yamasani742a6712011-05-04 14:49:28 -0700552 final long ident = Binder.clearCallingIdentity();
553 try {
554 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700555 options = cloneIfLocalBinder(options);
Amith Yamasani742a6712011-05-04 14:49:28 -0700556 ensureStateLoadedLocked();
557 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
558 if (id == null) {
559 throw new IllegalArgumentException("bad appWidgetId");
560 }
561 if (id.provider != null) {
562 throw new IllegalArgumentException("appWidgetId " + appWidgetId
563 + " already bound to " + id.provider.info.provider);
564 }
565 Provider p = lookupProviderLocked(provider);
566 if (p == null) {
567 throw new IllegalArgumentException("not a appwidget provider: " + provider);
568 }
569 if (p.zombie) {
570 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
571 + " safe mode: " + provider);
572 }
573
Amith Yamasani742a6712011-05-04 14:49:28 -0700574 id.provider = p;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700575 if (options == null) {
576 options = new Bundle();
577 }
578 id.options = options;
579
580 // We need to provide a default value for the widget category if it is not specified
581 if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
582 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
583 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
584 }
585
Amith Yamasani742a6712011-05-04 14:49:28 -0700586 p.instances.add(id);
587 int instancesSize = p.instances.size();
588 if (instancesSize == 1) {
589 // tell the provider that it's ready
590 sendEnableIntentLocked(p);
591 }
592
593 // send an update now -- We need this update now, and just for this appWidgetId.
594 // It's less critical when the next one happens, so when we schedule the next one,
595 // we add updatePeriodMillis to its start time. That time will have some slop,
596 // but that's okay.
597 sendUpdateIntentLocked(p, new int[] { appWidgetId });
598
599 // schedule the future updates
600 registerForBroadcastsLocked(p, getAppWidgetIds(p));
Adam Cohena1a2f962012-11-01 14:06:16 -0700601 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700602 }
603 } finally {
604 Binder.restoreCallingIdentity(ident);
605 }
606 }
607
Adam Cohen0aa2d422012-09-07 17:37:26 -0700608 public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka67a871d2012-11-01 18:26:01 -0700609 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET,
Michael Jurka61a5b012012-04-13 10:39:45 -0700610 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700611 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700612 }
613
614 public boolean bindAppWidgetIdIfAllowed(
Adam Cohen0aa2d422012-09-07 17:37:26 -0700615 String packageName, int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700616 try {
Michael Jurka67a871d2012-11-01 18:26:01 -0700617 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
Michael Jurka61a5b012012-04-13 10:39:45 -0700618 } catch (SecurityException se) {
619 if (!callerHasBindAppWidgetPermission(packageName)) {
620 return false;
621 }
622 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700623 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700624 return true;
625 }
626
627 private boolean callerHasBindAppWidgetPermission(String packageName) {
628 int callingUid = Binder.getCallingUid();
629 try {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700630 if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700631 return false;
632 }
633 } catch (Exception e) {
634 return false;
635 }
636 synchronized (mAppWidgetIds) {
637 ensureStateLoadedLocked();
638 return mPackagesWithBindWidgetPermission.contains(packageName);
639 }
640 }
641
642 public boolean hasBindAppWidgetPermission(String packageName) {
643 mContext.enforceCallingPermission(
644 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
645 "hasBindAppWidgetPermission packageName=" + packageName);
646
647 synchronized (mAppWidgetIds) {
648 ensureStateLoadedLocked();
649 return mPackagesWithBindWidgetPermission.contains(packageName);
650 }
651 }
652
653 public void setBindAppWidgetPermission(String packageName, boolean permission) {
654 mContext.enforceCallingPermission(
655 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
656 "setBindAppWidgetPermission packageName=" + packageName);
657
658 synchronized (mAppWidgetIds) {
659 ensureStateLoadedLocked();
660 if (permission) {
661 mPackagesWithBindWidgetPermission.add(packageName);
662 } else {
663 mPackagesWithBindWidgetPermission.remove(packageName);
664 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700665 saveStateAsync();
Michael Jurka61a5b012012-04-13 10:39:45 -0700666 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700667 }
668
Amith Yamasani742a6712011-05-04 14:49:28 -0700669 // Binds to a specific RemoteViewsService
670 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
671 synchronized (mAppWidgetIds) {
672 ensureStateLoadedLocked();
673 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
674 if (id == null) {
675 throw new IllegalArgumentException("bad appWidgetId");
676 }
677 final ComponentName componentName = intent.getComponent();
678 try {
Amith Yamasani98edc952012-09-25 14:09:27 -0700679 final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
680 PackageManager.GET_PERMISSIONS, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700681 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
682 throw new SecurityException("Selected service does not require "
683 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
684 }
Amith Yamasani98edc952012-09-25 14:09:27 -0700685 } catch (RemoteException e) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700686 throw new IllegalArgumentException("Unknown component " + componentName);
687 }
688
689 // If there is already a connection made for this service intent, then disconnect from
690 // that first. (This does not allow multiple connections to the same service under
691 // the same key)
692 ServiceConnectionProxy conn = null;
693 FilterComparison fc = new FilterComparison(intent);
694 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
695 if (mBoundRemoteViewsServices.containsKey(key)) {
696 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
697 conn.disconnect();
698 mContext.unbindService(conn);
699 mBoundRemoteViewsServices.remove(key);
700 }
701
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700702 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani791f8772012-11-21 14:06:07 -0800703 if (userId != mUserId) {
704 Slog.w(TAG, "AppWidgetServiceImpl of user " + mUserId
705 + " binding to provider on user " + userId);
706 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700707 // Bind to the RemoteViewsService (which will trigger a callback to the
708 // RemoteViewsAdapter.onServiceConnected())
709 final long token = Binder.clearCallingIdentity();
710 try {
711 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800712 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700713 mBoundRemoteViewsServices.put(key, conn);
714 } finally {
715 Binder.restoreCallingIdentity(token);
716 }
717
718 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
719 // when we can call back to the RemoteViewsService later to destroy associated
720 // factories.
721 incrementAppWidgetServiceRefCount(appWidgetId, fc);
722 }
723 }
724
725 // Unbinds from a specific RemoteViewsService
726 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
727 synchronized (mAppWidgetIds) {
728 ensureStateLoadedLocked();
729 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
730 // RemoteViewsAdapter)
731 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
732 intent));
733 if (mBoundRemoteViewsServices.containsKey(key)) {
734 // We don't need to use the appWidgetId until after we are sure there is something
735 // to unbind. Note that this may mask certain issues with apps calling unbind()
736 // more than necessary.
737 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
738 if (id == null) {
739 throw new IllegalArgumentException("bad appWidgetId");
740 }
741
742 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
743 .get(key);
744 conn.disconnect();
745 mContext.unbindService(conn);
746 mBoundRemoteViewsServices.remove(key);
747 } else {
748 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
749 }
750 }
751 }
752
753 // Unbinds from a RemoteViewsService when we delete an app widget
754 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
755 int appWidgetId = id.appWidgetId;
756 // Unbind all connections to Services bound to this AppWidgetId
757 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
758 .iterator();
759 while (it.hasNext()) {
760 final Pair<Integer, Intent.FilterComparison> key = it.next();
761 if (key.first.intValue() == appWidgetId) {
762 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
763 .get(key);
764 conn.disconnect();
765 mContext.unbindService(conn);
766 it.remove();
767 }
768 }
769
770 // Check if we need to destroy any services (if no other app widgets are
771 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800772 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700773 }
774
775 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800776 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700777 final ServiceConnection conn = new ServiceConnection() {
778 @Override
779 public void onServiceConnected(ComponentName name, IBinder service) {
780 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
781 try {
782 cb.onDestroy(intent);
783 } catch (RemoteException e) {
784 e.printStackTrace();
785 } catch (RuntimeException e) {
786 e.printStackTrace();
787 }
788 mContext.unbindService(this);
789 }
790
791 @Override
792 public void onServiceDisconnected(android.content.ComponentName name) {
793 // Do nothing
794 }
795 };
796
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700797 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700798 // Bind to the service and remove the static intent->factory mapping in the
799 // RemoteViewsService.
800 final long token = Binder.clearCallingIdentity();
801 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800802 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700803 } finally {
804 Binder.restoreCallingIdentity(token);
805 }
806 }
807
808 // Adds to the ref-count for a given RemoteViewsService intent
809 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
810 HashSet<Integer> appWidgetIds = null;
811 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
812 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
813 } else {
814 appWidgetIds = new HashSet<Integer>();
815 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
816 }
817 appWidgetIds.add(appWidgetId);
818 }
819
820 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
821 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800822 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700823 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
824 while (it.hasNext()) {
825 final FilterComparison key = it.next();
826 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800827 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700828 // If we have removed the last app widget referencing this service, then we
829 // should destroy it and remove it from this set
830 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800831 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700832 it.remove();
833 }
834 }
835 }
836 }
837
838 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
839 synchronized (mAppWidgetIds) {
840 ensureStateLoadedLocked();
841 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
842 if (id != null && id.provider != null && !id.provider.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700843 return cloneIfLocalBinder(id.provider.info);
Amith Yamasani742a6712011-05-04 14:49:28 -0700844 }
845 return null;
846 }
847 }
848
849 public RemoteViews getAppWidgetViews(int appWidgetId) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700850 if (DBG) log("getAppWidgetViews id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700851 synchronized (mAppWidgetIds) {
852 ensureStateLoadedLocked();
853 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
854 if (id != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700855 return cloneIfLocalBinder(id.views);
Amith Yamasani742a6712011-05-04 14:49:28 -0700856 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700857 if (DBG) log(" couldn't find appwidgetid");
Amith Yamasani742a6712011-05-04 14:49:28 -0700858 return null;
859 }
860 }
861
862 public List<AppWidgetProviderInfo> getInstalledProviders() {
Adam Cohen3fcc6b22012-11-15 14:10:07 -0800863 return getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
864 }
865
866 private List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700867 synchronized (mAppWidgetIds) {
868 ensureStateLoadedLocked();
869 final int N = mInstalledProviders.size();
870 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
871 for (int i = 0; i < N; i++) {
872 Provider p = mInstalledProviders.get(i);
Adam Cohen3fcc6b22012-11-15 14:10:07 -0800873 if (!p.zombie && (p.info.widgetCategory & categoryFilter) != 0) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700874 result.add(cloneIfLocalBinder(p.info));
Amith Yamasani742a6712011-05-04 14:49:28 -0700875 }
876 }
877 return result;
878 }
879 }
880
881 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
882 if (appWidgetIds == null) {
883 return;
884 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700885 if (DBG) log("updateAppWidgetIds views: " + views);
Adam Cohenf08a8b72012-07-16 12:02:10 -0700886 int bitmapMemoryUsage = 0;
887 if (views != null) {
888 bitmapMemoryUsage = views.estimateMemoryUsage();
889 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700890 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
891 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
892 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
893 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
894 " fill the device's screen once.");
895 }
896
Amith Yamasani742a6712011-05-04 14:49:28 -0700897 if (appWidgetIds.length == 0) {
898 return;
899 }
900 final int N = appWidgetIds.length;
901
902 synchronized (mAppWidgetIds) {
903 ensureStateLoadedLocked();
904 for (int i = 0; i < N; i++) {
905 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
906 updateAppWidgetInstanceLocked(id, views);
907 }
908 }
909 }
910
Adam Cohena1a2f962012-11-01 14:06:16 -0700911 private void saveStateAsync() {
912 mSaveStateHandler.post(mSaveStateRunnable);
913 }
914
915 private final Runnable mSaveStateRunnable = new Runnable() {
916 @Override
917 public void run() {
918 synchronized (mAppWidgetIds) {
919 ensureStateLoadedLocked();
920 saveStateLocked();
921 }
922 }
923 };
924
Adam Cohend2097eb2012-05-01 18:10:28 -0700925 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700926 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700927 options = cloneIfLocalBinder(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700928 ensureStateLoadedLocked();
929 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
930
931 if (id == null) {
932 return;
933 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700934
Adam Cohene8724c82012-04-19 17:11:40 -0700935 Provider p = id.provider;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700936 // Merge the options
937 id.options.putAll(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700938
939 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700940 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700941 intent.setComponent(p.info.provider);
942 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700943 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700944 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Adam Cohena1a2f962012-11-01 14:06:16 -0700945 saveStateAsync();
Adam Cohene8724c82012-04-19 17:11:40 -0700946 }
947 }
948
Adam Cohend2097eb2012-05-01 18:10:28 -0700949 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -0700950 synchronized (mAppWidgetIds) {
951 ensureStateLoadedLocked();
952 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700953 if (id != null && id.options != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700954 return cloneIfLocalBinder(id.options);
Adam Cohene8724c82012-04-19 17:11:40 -0700955 } else {
956 return Bundle.EMPTY;
957 }
958 }
959 }
960
Amith Yamasani742a6712011-05-04 14:49:28 -0700961 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
962 if (appWidgetIds == null) {
963 return;
964 }
965 if (appWidgetIds.length == 0) {
966 return;
967 }
968 final int N = appWidgetIds.length;
969
970 synchronized (mAppWidgetIds) {
971 ensureStateLoadedLocked();
972 for (int i = 0; i < N; i++) {
973 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Amith Yamasani791f8772012-11-21 14:06:07 -0800974 if (id == null) {
975 String message = "AppWidgetId NPE: mUserId=" + mUserId
976 + ", callingUid=" + Binder.getCallingUid()
977 + ", appWidgetIds[i]=" + appWidgetIds[i]
978 + "\n mAppWidgets:\n" + getUserWidgets();
979 throw new NullPointerException(message);
980 }
Winson Chung66119882012-10-11 14:26:25 -0700981 if (id.views != null) {
982 // 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
Amith Yamasani791f8772012-11-21 14:06:07 -0800989 private String getUserWidgets() {
990 StringBuffer sb = new StringBuffer();
991 for (AppWidgetId widget: mAppWidgetIds) {
992 sb.append(" id="); sb.append(widget.appWidgetId);
993 sb.append(", hostUid="); sb.append(widget.host.uid);
994 sb.append(", provider="); sb.append(widget.provider.info.provider.toString());
995 sb.append("\n");
996 }
997 sb.append("\n");
998 return sb.toString();
999 }
1000
Amith Yamasani742a6712011-05-04 14:49:28 -07001001 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
1002 if (appWidgetIds == null) {
1003 return;
1004 }
1005 if (appWidgetIds.length == 0) {
1006 return;
1007 }
1008 final int N = appWidgetIds.length;
1009
1010 synchronized (mAppWidgetIds) {
1011 ensureStateLoadedLocked();
1012 for (int i = 0; i < N; i++) {
1013 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
1014 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
1015 }
1016 }
1017 }
1018
1019 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
1020 synchronized (mAppWidgetIds) {
1021 ensureStateLoadedLocked();
1022 Provider p = lookupProviderLocked(provider);
1023 if (p == null) {
1024 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
1025 return;
1026 }
1027 ArrayList<AppWidgetId> instances = p.instances;
1028 final int callingUid = Binder.getCallingUid();
1029 final int N = instances.size();
1030 for (int i = 0; i < N; i++) {
1031 AppWidgetId id = instances.get(i);
1032 if (canAccessAppWidgetId(id, callingUid)) {
1033 updateAppWidgetInstanceLocked(id, views);
1034 }
1035 }
1036 }
1037 }
1038
1039 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
1040 updateAppWidgetInstanceLocked(id, views, false);
1041 }
1042
1043 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
1044 // allow for stale appWidgetIds and other badness
1045 // lookup also checks that the calling process can access the appWidgetId
1046 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1047 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1048
Winson Chung66119882012-10-11 14:26:25 -07001049 if (!isPartialUpdate) {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001050 // For a full update we replace the RemoteViews completely.
Amith Yamasani742a6712011-05-04 14:49:28 -07001051 id.views = views;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001052 } else {
1053 // For a partial update, we merge the new RemoteViews with the old.
1054 id.views.mergeRemoteViews(views);
1055 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001056
1057 // is anyone listening?
1058 if (id.host.callbacks != null) {
1059 try {
1060 // the lock is held, but this is a oneway call
1061 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
1062 } catch (RemoteException e) {
1063 // It failed; remove the callback. No need to prune because
1064 // we know that this host is still referenced by this instance.
1065 id.host.callbacks = null;
1066 }
1067 }
1068 }
1069 }
1070
1071 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1072 // allow for stale appWidgetIds and other badness
1073 // lookup also checks that the calling process can access the appWidgetId
1074 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1075 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1076 // is anyone listening?
1077 if (id.host.callbacks != null) {
1078 try {
1079 // the lock is held, but this is a oneway call
1080 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1081 } catch (RemoteException e) {
1082 // It failed; remove the callback. No need to prune because
1083 // we know that this host is still referenced by this instance.
1084 id.host.callbacks = null;
1085 }
1086 }
1087
1088 // If the host is unavailable, then we call the associated
1089 // RemoteViewsFactory.onDataSetChanged() directly
1090 if (id.host.callbacks == null) {
1091 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1092 for (FilterComparison key : keys) {
1093 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1094 Intent intent = key.getIntent();
1095
1096 final ServiceConnection conn = new ServiceConnection() {
1097 @Override
1098 public void onServiceConnected(ComponentName name, IBinder service) {
1099 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1100 .asInterface(service);
1101 try {
1102 cb.onDataSetChangedAsync();
1103 } catch (RemoteException e) {
1104 e.printStackTrace();
1105 } catch (RuntimeException e) {
1106 e.printStackTrace();
1107 }
1108 mContext.unbindService(this);
1109 }
1110
1111 @Override
1112 public void onServiceDisconnected(android.content.ComponentName name) {
1113 // Do nothing
1114 }
1115 };
1116
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001117 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001118 // Bind to the service and call onDataSetChanged()
1119 final long token = Binder.clearCallingIdentity();
1120 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001121 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001122 } finally {
1123 Binder.restoreCallingIdentity(token);
1124 }
1125 }
1126 }
1127 }
1128 }
1129 }
1130
Adam Cohen3ff2d862012-09-26 14:07:57 -07001131 private boolean isLocalBinder() {
1132 return Process.myPid() == Binder.getCallingPid();
1133 }
1134
1135 private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1136 if (isLocalBinder() && rv != null) {
1137 return rv.clone();
1138 }
1139 return rv;
1140 }
1141
1142 private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
1143 if (isLocalBinder() && info != null) {
1144 return info.clone();
1145 }
1146 return info;
1147 }
1148
1149 private Bundle cloneIfLocalBinder(Bundle bundle) {
1150 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
1151 // if we start adding objects to the options. Further, it would only be an issue if keyguard
1152 // used such options.
1153 if (isLocalBinder() && bundle != null) {
1154 return (Bundle) bundle.clone();
1155 }
1156 return bundle;
1157 }
1158
Amith Yamasani742a6712011-05-04 14:49:28 -07001159 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1160 List<RemoteViews> updatedViews) {
1161 int callingUid = enforceCallingUid(packageName);
1162 synchronized (mAppWidgetIds) {
1163 ensureStateLoadedLocked();
1164 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1165 host.callbacks = callbacks;
1166
1167 updatedViews.clear();
1168
1169 ArrayList<AppWidgetId> instances = host.instances;
1170 int N = instances.size();
1171 int[] updatedIds = new int[N];
1172 for (int i = 0; i < N; i++) {
1173 AppWidgetId id = instances.get(i);
1174 updatedIds[i] = id.appWidgetId;
Adam Cohen3ff2d862012-09-26 14:07:57 -07001175 updatedViews.add(cloneIfLocalBinder(id.views));
Amith Yamasani742a6712011-05-04 14:49:28 -07001176 }
1177 return updatedIds;
1178 }
1179 }
1180
1181 public void stopListening(int hostId) {
1182 synchronized (mAppWidgetIds) {
1183 ensureStateLoadedLocked();
1184 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1185 if (host != null) {
1186 host.callbacks = null;
1187 pruneHostLocked(host);
1188 }
1189 }
1190 }
1191
1192 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1193 if (id.host.uid == callingUid) {
1194 // Apps hosting the AppWidget have access to it.
1195 return true;
1196 }
1197 if (id.provider != null && id.provider.uid == callingUid) {
1198 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1199 return true;
1200 }
1201 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1202 // Apps that can bind have access to all appWidgetIds.
1203 return true;
1204 }
1205 // Nobody else can access it.
1206 return false;
1207 }
1208
1209 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1210 int callingUid = Binder.getCallingUid();
1211 final int N = mAppWidgetIds.size();
1212 for (int i = 0; i < N; i++) {
1213 AppWidgetId id = mAppWidgetIds.get(i);
1214 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1215 return id;
1216 }
1217 }
1218 return null;
1219 }
1220
1221 Provider lookupProviderLocked(ComponentName provider) {
1222 final int N = mInstalledProviders.size();
1223 for (int i = 0; i < N; i++) {
1224 Provider p = mInstalledProviders.get(i);
1225 if (p.info.provider.equals(provider)) {
1226 return p;
1227 }
1228 }
1229 return null;
1230 }
1231
1232 Host lookupHostLocked(int uid, 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.uid == uid && h.hostId == hostId) {
1237 return h;
1238 }
1239 }
1240 return null;
1241 }
1242
1243 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1244 final int N = mHosts.size();
1245 for (int i = 0; i < N; i++) {
1246 Host h = mHosts.get(i);
1247 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1248 return h;
1249 }
1250 }
1251 Host host = new Host();
1252 host.packageName = packageName;
1253 host.uid = uid;
1254 host.hostId = hostId;
1255 mHosts.add(host);
1256 return host;
1257 }
1258
1259 void pruneHostLocked(Host host) {
1260 if (host.instances.size() == 0 && host.callbacks == null) {
1261 mHosts.remove(host);
1262 }
1263 }
1264
Adam Cohena1a2f962012-11-01 14:06:16 -07001265 void loadAppWidgetListLocked() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001266 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001267 try {
1268 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1269 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1270 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001271
Amith Yamasani483f3b02012-03-13 16:08:00 -07001272 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1273 for (int i = 0; i < N; i++) {
1274 ResolveInfo ri = broadcastReceivers.get(i);
1275 addProviderLocked(ri);
1276 }
1277 } catch (RemoteException re) {
1278 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001279 }
1280 }
1281
1282 boolean addProviderLocked(ResolveInfo ri) {
1283 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1284 return false;
1285 }
1286 if (!ri.activityInfo.isEnabled()) {
1287 return false;
1288 }
1289 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1290 ri.activityInfo.name), ri);
1291 if (p != null) {
1292 mInstalledProviders.add(p);
1293 return true;
1294 } else {
1295 return false;
1296 }
1297 }
1298
1299 void removeProviderLocked(int index, Provider p) {
1300 int N = p.instances.size();
1301 for (int i = 0; i < N; i++) {
1302 AppWidgetId id = p.instances.get(i);
1303 // Call back with empty RemoteViews
1304 updateAppWidgetInstanceLocked(id, null);
1305 // Stop telling the host about updates for this from now on
1306 cancelBroadcasts(p);
1307 // clear out references to this appWidgetId
1308 id.host.instances.remove(id);
1309 mAppWidgetIds.remove(id);
1310 id.provider = null;
1311 pruneHostLocked(id.host);
1312 id.host = null;
1313 }
1314 p.instances.clear();
1315 mInstalledProviders.remove(index);
1316 mDeletedProviders.add(p);
1317 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1318 cancelBroadcasts(p);
1319 }
1320
1321 void sendEnableIntentLocked(Provider p) {
1322 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1323 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001324 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001325 }
1326
1327 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1328 if (appWidgetIds != null && appWidgetIds.length > 0) {
1329 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1330 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1331 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001332 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001333 }
1334 }
1335
1336 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1337 if (p.info.updatePeriodMillis > 0) {
1338 // if this is the first instance, set the alarm. otherwise,
1339 // rely on the fact that we've already set it and that
1340 // PendingIntent.getBroadcast will update the extras.
1341 boolean alreadyRegistered = p.broadcast != null;
1342 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1343 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1344 intent.setComponent(p.info.provider);
1345 long token = Binder.clearCallingIdentity();
1346 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001347 p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
1348 PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001349 } finally {
1350 Binder.restoreCallingIdentity(token);
1351 }
1352 if (!alreadyRegistered) {
1353 long period = p.info.updatePeriodMillis;
1354 if (period < MIN_UPDATE_PERIOD) {
1355 period = MIN_UPDATE_PERIOD;
1356 }
1357 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1358 .elapsedRealtime()
1359 + period, period, p.broadcast);
1360 }
1361 }
1362 }
1363
1364 static int[] getAppWidgetIds(Provider p) {
1365 int instancesSize = p.instances.size();
1366 int appWidgetIds[] = new int[instancesSize];
1367 for (int i = 0; i < instancesSize; i++) {
1368 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1369 }
1370 return appWidgetIds;
1371 }
1372
1373 public int[] getAppWidgetIds(ComponentName provider) {
1374 synchronized (mAppWidgetIds) {
1375 ensureStateLoadedLocked();
1376 Provider p = lookupProviderLocked(provider);
1377 if (p != null && Binder.getCallingUid() == p.uid) {
1378 return getAppWidgetIds(p);
1379 } else {
1380 return new int[0];
1381 }
1382 }
1383 }
1384
Michael Jurka75b5cfb2012-11-15 18:22:47 -08001385 static int[] getAppWidgetIds(Host h) {
1386 int instancesSize = h.instances.size();
1387 int appWidgetIds[] = new int[instancesSize];
1388 for (int i = 0; i < instancesSize; i++) {
1389 appWidgetIds[i] = h.instances.get(i).appWidgetId;
1390 }
1391 return appWidgetIds;
1392 }
1393
1394 public int[] getAppWidgetIdsForHost(int hostId) {
1395 synchronized (mAppWidgetIds) {
1396 ensureStateLoadedLocked();
1397 int callingUid = Binder.getCallingUid();
1398 Host host = lookupHostLocked(callingUid, hostId);
1399 if (host != null) {
1400 return getAppWidgetIds(host);
1401 } else {
1402 return new int[0];
1403 }
1404 }
1405 }
1406
Amith Yamasani742a6712011-05-04 14:49:28 -07001407 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1408 Provider p = null;
1409
1410 ActivityInfo activityInfo = ri.activityInfo;
1411 XmlResourceParser parser = null;
1412 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001413 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001414 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1415 if (parser == null) {
1416 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1417 + " meta-data for " + "AppWidget provider '" + component + '\'');
1418 return null;
1419 }
1420
1421 AttributeSet attrs = Xml.asAttributeSet(parser);
1422
1423 int type;
1424 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1425 && type != XmlPullParser.START_TAG) {
1426 // drain whitespace, comments, etc.
1427 }
1428
1429 String nodeName = parser.getName();
1430 if (!"appwidget-provider".equals(nodeName)) {
1431 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1432 + " AppWidget provider '" + component + '\'');
1433 return null;
1434 }
1435
1436 p = new Provider();
1437 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1438 info.provider = component;
1439 p.uid = activityInfo.applicationInfo.uid;
1440
Amith Yamasani483f3b02012-03-13 16:08:00 -07001441 Resources res = mContext.getPackageManager()
Amith Yamasani8320de82012-10-05 16:10:38 -07001442 .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001443
1444 TypedArray sa = res.obtainAttributes(attrs,
1445 com.android.internal.R.styleable.AppWidgetProviderInfo);
1446
1447 // These dimensions has to be resolved in the application's context.
1448 // We simply send back the raw complex data, which will be
1449 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1450 TypedValue value = sa
1451 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1452 info.minWidth = value != null ? value.data : 0;
1453 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1454 info.minHeight = value != null ? value.data : 0;
1455 value = sa.peekValue(
1456 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1457 info.minResizeWidth = value != null ? value.data : info.minWidth;
1458 value = sa.peekValue(
1459 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1460 info.minResizeHeight = value != null ? value.data : info.minHeight;
1461 info.updatePeriodMillis = sa.getInt(
1462 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1463 info.initialLayout = sa.getResourceId(
1464 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001465 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1466 AppWidgetProviderInfo_initialKeyguardLayout, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001467 String className = sa
1468 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1469 if (className != null) {
1470 info.configure = new ComponentName(component.getPackageName(), className);
1471 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001472 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001473 info.icon = ri.getIconResource();
1474 info.previewImage = sa.getResourceId(
1475 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1476 info.autoAdvanceViewId = sa.getResourceId(
1477 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1478 info.resizeMode = sa.getInt(
1479 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1480 AppWidgetProviderInfo.RESIZE_NONE);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001481 info.widgetCategory = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001482 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001483 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
Amith Yamasani742a6712011-05-04 14:49:28 -07001484
1485 sa.recycle();
1486 } catch (Exception e) {
1487 // Ok to catch Exception here, because anything going wrong because
1488 // of what a client process passes to us should not be fatal for the
1489 // system process.
1490 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1491 return null;
1492 } finally {
1493 if (parser != null)
1494 parser.close();
1495 }
1496 return p;
1497 }
1498
1499 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001500 PackageInfo pkgInfo = null;
1501 try {
1502 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1503 } catch (RemoteException re) {
1504 // Shouldn't happen, local call
1505 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001506 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1507 throw new PackageManager.NameNotFoundException();
1508 }
1509 return pkgInfo.applicationInfo.uid;
1510 }
1511
Jim Millerf229e4d32012-09-12 20:32:50 -07001512 int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1513 int callingUid = Binder.getCallingUid();
Michael Jurka03bdc8a2012-09-21 16:10:21 -07001514 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
Jim Millerf229e4d32012-09-12 20:32:50 -07001515 return callingUid;
1516 }
1517 return enforceCallingUid(packageName);
1518 }
1519
Amith Yamasani742a6712011-05-04 14:49:28 -07001520 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1521 int callingUid = Binder.getCallingUid();
1522 int packageUid;
1523 try {
1524 packageUid = getUidForPackage(packageName);
1525 } catch (PackageManager.NameNotFoundException ex) {
1526 throw new IllegalArgumentException("packageName and uid don't match packageName="
1527 + packageName);
1528 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001529 if (!UserHandle.isSameApp(callingUid, packageUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001530 throw new IllegalArgumentException("packageName and uid don't match packageName="
1531 + packageName);
1532 }
1533 return callingUid;
1534 }
1535
1536 void sendInitialBroadcasts() {
1537 synchronized (mAppWidgetIds) {
1538 ensureStateLoadedLocked();
1539 final int N = mInstalledProviders.size();
1540 for (int i = 0; i < N; i++) {
1541 Provider p = mInstalledProviders.get(i);
1542 if (p.instances.size() > 0) {
1543 sendEnableIntentLocked(p);
1544 int[] appWidgetIds = getAppWidgetIds(p);
1545 sendUpdateIntentLocked(p, appWidgetIds);
1546 registerForBroadcastsLocked(p, appWidgetIds);
1547 }
1548 }
1549 }
1550 }
1551
1552 // only call from initialization -- it assumes that the data structures are all empty
1553 void loadStateLocked() {
1554 AtomicFile file = savedStateFile();
1555 try {
1556 FileInputStream stream = file.openRead();
1557 readStateFromFileLocked(stream);
1558
1559 if (stream != null) {
1560 try {
1561 stream.close();
1562 } catch (IOException e) {
1563 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1564 }
1565 }
1566 } catch (FileNotFoundException e) {
1567 Slog.w(TAG, "Failed to read state: " + e);
1568 }
1569 }
1570
1571 void saveStateLocked() {
1572 AtomicFile file = savedStateFile();
1573 FileOutputStream stream;
1574 try {
1575 stream = file.startWrite();
1576 if (writeStateToFileLocked(stream)) {
1577 file.finishWrite(stream);
1578 } else {
1579 file.failWrite(stream);
1580 Slog.w(TAG, "Failed to save state, restoring backup.");
1581 }
1582 } catch (IOException e) {
1583 Slog.w(TAG, "Failed open state file for write: " + e);
1584 }
1585 }
1586
1587 boolean writeStateToFileLocked(FileOutputStream stream) {
1588 int N;
1589
1590 try {
1591 XmlSerializer out = new FastXmlSerializer();
1592 out.setOutput(stream, "utf-8");
1593 out.startDocument(null, true);
1594 out.startTag(null, "gs");
1595
1596 int providerIndex = 0;
1597 N = mInstalledProviders.size();
1598 for (int i = 0; i < N; i++) {
1599 Provider p = mInstalledProviders.get(i);
1600 if (p.instances.size() > 0) {
1601 out.startTag(null, "p");
1602 out.attribute(null, "pkg", p.info.provider.getPackageName());
1603 out.attribute(null, "cl", p.info.provider.getClassName());
1604 out.endTag(null, "p");
1605 p.tag = providerIndex;
1606 providerIndex++;
1607 }
1608 }
1609
1610 N = mHosts.size();
1611 for (int i = 0; i < N; i++) {
1612 Host host = mHosts.get(i);
1613 out.startTag(null, "h");
1614 out.attribute(null, "pkg", host.packageName);
1615 out.attribute(null, "id", Integer.toHexString(host.hostId));
1616 out.endTag(null, "h");
1617 host.tag = i;
1618 }
1619
1620 N = mAppWidgetIds.size();
1621 for (int i = 0; i < N; i++) {
1622 AppWidgetId id = mAppWidgetIds.get(i);
1623 out.startTag(null, "g");
1624 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1625 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1626 if (id.provider != null) {
1627 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1628 }
Adam Cohen0aa2d422012-09-07 17:37:26 -07001629 if (id.options != null) {
1630 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1631 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1632 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1633 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1634 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1635 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1636 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1637 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1638 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1639 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1640 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001641 out.endTag(null, "g");
1642 }
1643
Michael Jurka61a5b012012-04-13 10:39:45 -07001644 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1645 while (it.hasNext()) {
1646 out.startTag(null, "b");
1647 out.attribute(null, "packageName", it.next());
1648 out.endTag(null, "b");
1649 }
1650
Amith Yamasani742a6712011-05-04 14:49:28 -07001651 out.endTag(null, "gs");
1652
1653 out.endDocument();
1654 return true;
1655 } catch (IOException e) {
1656 Slog.w(TAG, "Failed to write state: " + e);
1657 return false;
1658 }
1659 }
1660
Adam Cohen0aa2d422012-09-07 17:37:26 -07001661 @SuppressWarnings("unused")
Amith Yamasani742a6712011-05-04 14:49:28 -07001662 void readStateFromFileLocked(FileInputStream stream) {
1663 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001664 try {
1665 XmlPullParser parser = Xml.newPullParser();
1666 parser.setInput(stream, null);
1667
1668 int type;
1669 int providerIndex = 0;
1670 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1671 do {
1672 type = parser.next();
1673 if (type == XmlPullParser.START_TAG) {
1674 String tag = parser.getName();
1675 if ("p".equals(tag)) {
1676 // TODO: do we need to check that this package has the same signature
1677 // as before?
1678 String pkg = parser.getAttributeValue(null, "pkg");
1679 String cl = parser.getAttributeValue(null, "cl");
1680
Amith Yamasanif203aee2012-08-29 18:41:53 -07001681 final IPackageManager packageManager = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -07001682 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001683 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
Amith Yamasanif203aee2012-08-29 18:41:53 -07001684 } catch (RemoteException e) {
1685 String[] pkgs = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001686 .currentToCanonicalPackageNames(new String[] { pkg });
1687 pkg = pkgs[0];
1688 }
1689
1690 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1691 if (p == null && mSafeMode) {
1692 // if we're in safe mode, make a temporary one
1693 p = new Provider();
1694 p.info = new AppWidgetProviderInfo();
1695 p.info.provider = new ComponentName(pkg, cl);
1696 p.zombie = true;
1697 mInstalledProviders.add(p);
1698 }
1699 if (p != null) {
1700 // if it wasn't uninstalled or something
1701 loadedProviders.put(providerIndex, p);
1702 }
1703 providerIndex++;
1704 } else if ("h".equals(tag)) {
1705 Host host = new Host();
1706
1707 // TODO: do we need to check that this package has the same signature
1708 // as before?
1709 host.packageName = parser.getAttributeValue(null, "pkg");
1710 try {
1711 host.uid = getUidForPackage(host.packageName);
1712 } catch (PackageManager.NameNotFoundException ex) {
1713 host.zombie = true;
1714 }
1715 if (!host.zombie || mSafeMode) {
1716 // In safe mode, we don't discard the hosts we don't recognize
1717 // so that they're not pruned from our list. Otherwise, we do.
1718 host.hostId = Integer
1719 .parseInt(parser.getAttributeValue(null, "id"), 16);
1720 mHosts.add(host);
1721 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001722 } else if ("b".equals(tag)) {
1723 String packageName = parser.getAttributeValue(null, "packageName");
1724 if (packageName != null) {
1725 mPackagesWithBindWidgetPermission.add(packageName);
1726 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001727 } else if ("g".equals(tag)) {
1728 AppWidgetId id = new AppWidgetId();
1729 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1730 if (id.appWidgetId >= mNextAppWidgetId) {
1731 mNextAppWidgetId = id.appWidgetId + 1;
1732 }
1733
Adam Cohen0aa2d422012-09-07 17:37:26 -07001734 Bundle options = new Bundle();
1735 String minWidthString = parser.getAttributeValue(null, "min_width");
1736 if (minWidthString != null) {
1737 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1738 Integer.parseInt(minWidthString, 16));
1739 }
1740 String minHeightString = parser.getAttributeValue(null, "min_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001741 if (minHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001742 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1743 Integer.parseInt(minHeightString, 16));
1744 }
Adam Cohendb38d8a2012-09-21 18:14:58 -07001745 String maxWidthString = parser.getAttributeValue(null, "max_width");
1746 if (maxWidthString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001747 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1748 Integer.parseInt(maxWidthString, 16));
1749 }
1750 String maxHeightString = parser.getAttributeValue(null, "max_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001751 if (maxHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001752 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1753 Integer.parseInt(maxHeightString, 16));
1754 }
1755 String categoryString = parser.getAttributeValue(null, "host_category");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001756 if (categoryString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001757 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1758 Integer.parseInt(categoryString, 16));
1759 }
1760 id.options = options;
1761
Amith Yamasani742a6712011-05-04 14:49:28 -07001762 String providerString = parser.getAttributeValue(null, "p");
1763 if (providerString != null) {
1764 // there's no provider if it hasn't been bound yet.
1765 // maybe we don't have to save this, but it brings the system
1766 // to the state it was in.
1767 int pIndex = Integer.parseInt(providerString, 16);
1768 id.provider = loadedProviders.get(pIndex);
1769 if (false) {
1770 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1771 + pIndex + " which is " + id.provider);
1772 }
1773 if (id.provider == null) {
1774 // This provider is gone. We just let the host figure out
1775 // that this happened when it fails to load it.
1776 continue;
1777 }
1778 }
1779
1780 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1781 id.host = mHosts.get(hIndex);
1782 if (id.host == null) {
1783 // This host is gone.
1784 continue;
1785 }
1786
1787 if (id.provider != null) {
1788 id.provider.instances.add(id);
1789 }
1790 id.host.instances.add(id);
1791 mAppWidgetIds.add(id);
1792 }
1793 }
1794 } while (type != XmlPullParser.END_DOCUMENT);
1795 success = true;
1796 } catch (NullPointerException e) {
1797 Slog.w(TAG, "failed parsing " + e);
1798 } catch (NumberFormatException e) {
1799 Slog.w(TAG, "failed parsing " + e);
1800 } catch (XmlPullParserException e) {
1801 Slog.w(TAG, "failed parsing " + e);
1802 } catch (IOException e) {
1803 Slog.w(TAG, "failed parsing " + e);
1804 } catch (IndexOutOfBoundsException e) {
1805 Slog.w(TAG, "failed parsing " + e);
1806 }
1807
1808 if (success) {
1809 // delete any hosts that didn't manage to get connected (should happen)
1810 // if it matters, they'll be reconnected.
1811 for (int i = mHosts.size() - 1; i >= 0; i--) {
1812 pruneHostLocked(mHosts.get(i));
1813 }
1814 } else {
1815 // failed reading, clean up
1816 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1817
1818 mAppWidgetIds.clear();
1819 mHosts.clear();
1820 final int N = mInstalledProviders.size();
1821 for (int i = 0; i < N; i++) {
1822 mInstalledProviders.get(i).instances.clear();
1823 }
1824 }
1825 }
1826
Amith Yamasani13593602012-03-22 16:16:17 -07001827 static File getSettingsFile(int userId) {
Amith Yamasani61f57372012-08-31 12:12:28 -07001828 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
Amith Yamasani13593602012-03-22 16:16:17 -07001829 }
1830
Amith Yamasani742a6712011-05-04 14:49:28 -07001831 AtomicFile savedStateFile() {
Amith Yamasani61f57372012-08-31 12:12:28 -07001832 File dir = Environment.getUserSystemDirectory(mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001833 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001834 if (!settingsFile.exists() && mUserId == 0) {
1835 if (!dir.exists()) {
1836 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001837 }
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001838 // Migrate old data
1839 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1840 // Method doesn't throw an exception on failure. Ignore any errors
1841 // in moving the file (like non-existence)
1842 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001843 }
1844 return new AtomicFile(settingsFile);
1845 }
1846
Amith Yamasani756901d2012-10-12 12:30:07 -07001847 void onUserStopping() {
Amith Yamasani13593602012-03-22 16:16:17 -07001848 // prune the ones we don't want to keep
1849 int N = mInstalledProviders.size();
1850 for (int i = N - 1; i >= 0; i--) {
1851 Provider p = mInstalledProviders.get(i);
1852 cancelBroadcasts(p);
1853 }
Amith Yamasani756901d2012-10-12 12:30:07 -07001854 }
1855
1856 void onUserRemoved() {
Amith Yamasani13593602012-03-22 16:16:17 -07001857 getSettingsFile(mUserId).delete();
1858 }
1859
Winson Chung7fbd2842012-06-13 10:35:51 -07001860 boolean addProvidersForPackageLocked(String pkgName) {
1861 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001862 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1863 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001864 List<ResolveInfo> broadcastReceivers;
1865 try {
1866 broadcastReceivers = mPm.queryIntentReceivers(intent,
1867 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1868 PackageManager.GET_META_DATA, mUserId);
1869 } catch (RemoteException re) {
1870 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001871 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001872 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001873 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1874 for (int i = 0; i < N; i++) {
1875 ResolveInfo ri = broadcastReceivers.get(i);
1876 ActivityInfo ai = ri.activityInfo;
1877 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1878 continue;
1879 }
1880 if (pkgName.equals(ai.packageName)) {
1881 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001882 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001883 }
1884 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001885
1886 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001887 }
1888
Winson Chunga3195052012-06-25 10:02:10 -07001889 /**
1890 * Updates all providers with the specified package names, and records any providers that were
1891 * pruned.
1892 *
1893 * @return whether any providers were updated
1894 */
1895 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001896 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001897 HashSet<String> keep = new HashSet<String>();
1898 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1899 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001900 List<ResolveInfo> broadcastReceivers;
1901 try {
1902 broadcastReceivers = mPm.queryIntentReceivers(intent,
1903 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1904 PackageManager.GET_META_DATA, mUserId);
1905 } catch (RemoteException re) {
1906 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001907 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001908 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001909
1910 // add the missing ones and collect which ones to keep
1911 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1912 for (int i = 0; i < N; i++) {
1913 ResolveInfo ri = broadcastReceivers.get(i);
1914 ActivityInfo ai = ri.activityInfo;
1915 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1916 continue;
1917 }
1918 if (pkgName.equals(ai.packageName)) {
1919 ComponentName component = new ComponentName(ai.packageName, ai.name);
1920 Provider p = lookupProviderLocked(component);
1921 if (p == null) {
1922 if (addProviderLocked(ri)) {
1923 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001924 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001925 }
1926 } else {
1927 Provider parsed = parseProviderInfoXml(component, ri);
1928 if (parsed != null) {
1929 keep.add(ai.name);
1930 // Use the new AppWidgetProviderInfo.
1931 p.info = parsed.info;
1932 // If it's enabled
1933 final int M = p.instances.size();
1934 if (M > 0) {
1935 int[] appWidgetIds = getAppWidgetIds(p);
1936 // Reschedule for the new updatePeriodMillis (don't worry about handling
1937 // it specially if updatePeriodMillis didn't change because we just sent
1938 // an update, and the next one will be updatePeriodMillis from now).
1939 cancelBroadcasts(p);
1940 registerForBroadcastsLocked(p, appWidgetIds);
1941 // If it's currently showing, call back with the new
1942 // AppWidgetProviderInfo.
1943 for (int j = 0; j < M; j++) {
1944 AppWidgetId id = p.instances.get(j);
1945 id.views = null;
1946 if (id.host != null && id.host.callbacks != null) {
1947 try {
1948 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1949 } catch (RemoteException ex) {
1950 // It failed; remove the callback. No need to prune because
1951 // we know that this host is still referenced by this
1952 // instance.
1953 id.host.callbacks = null;
1954 }
1955 }
1956 }
1957 // Now that we've told the host, push out an update.
1958 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07001959 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001960 }
1961 }
1962 }
1963 }
1964 }
1965
1966 // prune the ones we don't want to keep
1967 N = mInstalledProviders.size();
1968 for (int i = N - 1; i >= 0; i--) {
1969 Provider p = mInstalledProviders.get(i);
1970 if (pkgName.equals(p.info.provider.getPackageName())
1971 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07001972 if (removedProviders != null) {
1973 removedProviders.add(p.info.provider);
1974 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001975 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001976 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001977 }
1978 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001979
1980 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07001981 }
1982
Winson Chung7fbd2842012-06-13 10:35:51 -07001983 boolean removeProvidersForPackageLocked(String pkgName) {
1984 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001985 int N = mInstalledProviders.size();
1986 for (int i = N - 1; i >= 0; i--) {
1987 Provider p = mInstalledProviders.get(i);
1988 if (pkgName.equals(p.info.provider.getPackageName())) {
1989 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001990 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001991 }
1992 }
1993
1994 // Delete the hosts for this package too
1995 //
1996 // By now, we have removed any AppWidgets that were in any hosts here,
1997 // so we don't need to worry about sending DISABLE broadcasts to them.
1998 N = mHosts.size();
1999 for (int i = N - 1; i >= 0; i--) {
2000 Host host = mHosts.get(i);
2001 if (pkgName.equals(host.packageName)) {
2002 deleteHostLocked(host);
2003 }
2004 }
Winson Chung7fbd2842012-06-13 10:35:51 -07002005
2006 return providersRemoved;
2007 }
2008
2009 void notifyHostsForProvidersChangedLocked() {
2010 final int N = mHosts.size();
2011 for (int i = N - 1; i >= 0; i--) {
2012 Host host = mHosts.get(i);
2013 try {
2014 if (host.callbacks != null) {
2015 host.callbacks.providersChanged();
2016 }
2017 } catch (RemoteException ex) {
2018 // It failed; remove the callback. No need to prune because
2019 // we know that this host is still referenced by this
2020 // instance.
2021 host.callbacks = null;
2022 }
2023 }
Amith Yamasani742a6712011-05-04 14:49:28 -07002024 }
2025}