blob: fb2828b9cc391f1388ee11ae60ff3753837720f6 [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
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700180 final Context mContext;
181 final IPackageManager mPm;
182 final AlarmManager mAlarmManager;
183 final ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
184 final int mUserId;
185 final boolean mHasFeature;
186
Amith Yamasani742a6712011-05-04 14:49:28 -0700187 Locale mLocale;
Amith Yamasani742a6712011-05-04 14:49:28 -0700188 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
189 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700190 final ArrayList<Host> mHosts = new ArrayList<Host>();
Michael Jurka61a5b012012-04-13 10:39:45 -0700191 // set of package names
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700192 final HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
Amith Yamasani742a6712011-05-04 14:49:28 -0700193 boolean mSafeMode;
Amith Yamasani742a6712011-05-04 14:49:28 -0700194 boolean mStateLoaded;
Adam Cohen311c79c2012-05-10 14:44:38 -0700195 int mMaxWidgetBitmapMemory;
Amith Yamasani742a6712011-05-04 14:49:28 -0700196
Adam Cohena1a2f962012-11-01 14:06:16 -0700197 private final Handler mSaveStateHandler;
198
Amith Yamasani742a6712011-05-04 14:49:28 -0700199 // These are for debugging only -- widgets are going missing in some rare instances
200 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
201 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
202
Adam Cohena1a2f962012-11-01 14:06:16 -0700203 AppWidgetServiceImpl(Context context, int userId, Handler saveStateHandler) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700204 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700205 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700206 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
207 mUserId = userId;
Adam Cohena1a2f962012-11-01 14:06:16 -0700208 mSaveStateHandler = saveStateHandler;
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700209 mHasFeature = context.getPackageManager().hasSystemFeature(
210 PackageManager.FEATURE_APP_WIDGETS);
Adam Cohen311c79c2012-05-10 14:44:38 -0700211 computeMaximumWidgetBitmapMemory();
212 }
213
214 void computeMaximumWidgetBitmapMemory() {
215 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Jeff Browna8b9def2012-07-23 14:22:49 -0700216 Display display = wm.getDefaultDisplay();
217 Point size = new Point();
218 display.getRealSize(size);
Winson Chunge92aad42012-06-22 14:11:47 -0700219 // Cap memory usage at 1.5 times the size of the display
220 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
Jeff Browna8b9def2012-07-23 14:22:49 -0700221 mMaxWidgetBitmapMemory = 6 * size.x * size.y;
Amith Yamasani742a6712011-05-04 14:49:28 -0700222 }
223
224 public void systemReady(boolean safeMode) {
225 mSafeMode = safeMode;
226
227 synchronized (mAppWidgetIds) {
228 ensureStateLoadedLocked();
229 }
230 }
231
Amith Yamasani8320de82012-10-05 16:10:38 -0700232 private void log(String msg) {
233 Slog.i(TAG, "u=" + mUserId + ": " + msg);
234 }
235
Amith Yamasani742a6712011-05-04 14:49:28 -0700236 void onConfigurationChanged() {
Amith Yamasani8320de82012-10-05 16:10:38 -0700237 if (DBG) log("Got onConfigurationChanged()");
Amith Yamasani742a6712011-05-04 14:49:28 -0700238 Locale revised = Locale.getDefault();
239 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
240 mLocale = revised;
241
242 synchronized (mAppWidgetIds) {
243 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700244 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
245 // list of installed providers and skip providers that we don't need to update.
246 // Also note that remove the provider does not clear the Provider component data.
247 ArrayList<Provider> installedProviders =
248 new ArrayList<Provider>(mInstalledProviders);
249 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
250 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700251 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700252 Provider p = installedProviders.get(i);
253 ComponentName cn = p.info.provider;
254 if (!removedProviders.contains(cn)) {
255 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
256 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700257 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700258 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700259 }
260 }
261 }
262
263 void onBroadcastReceived(Intent intent) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700264 if (DBG) log("onBroadcast " + intent);
Amith Yamasani742a6712011-05-04 14:49:28 -0700265 final String action = intent.getAction();
266 boolean added = false;
267 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700268 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700269 String pkgList[] = null;
270 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
271 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
272 added = true;
273 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
274 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
275 added = false;
276 } else {
277 Uri uri = intent.getData();
278 if (uri == null) {
279 return;
280 }
281 String pkgName = uri.getSchemeSpecificPart();
282 if (pkgName == null) {
283 return;
284 }
285 pkgList = new String[] { pkgName };
286 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
287 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
288 }
289 if (pkgList == null || pkgList.length == 0) {
290 return;
291 }
292 if (added || changed) {
293 synchronized (mAppWidgetIds) {
294 ensureStateLoadedLocked();
295 Bundle extras = intent.getExtras();
296 if (changed
297 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
298 for (String pkgName : pkgList) {
299 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700300 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700301 }
302 } else {
303 // The package was just added
304 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700305 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700306 }
307 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700308 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700309 }
310 } else {
311 Bundle extras = intent.getExtras();
312 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
313 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
314 } else {
315 synchronized (mAppWidgetIds) {
316 ensureStateLoadedLocked();
317 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700318 providersModified |= removeProvidersForPackageLocked(pkgName);
Adam Cohena1a2f962012-11-01 14:06:16 -0700319 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700320 }
321 }
322 }
323 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700324
325 if (providersModified) {
326 // If the set of providers has been modified, notify each active AppWidgetHost
327 synchronized (mAppWidgetIds) {
328 ensureStateLoadedLocked();
329 notifyHostsForProvidersChangedLocked();
330 }
331 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700332 }
333
334 private void dumpProvider(Provider p, int index, PrintWriter pw) {
335 AppWidgetProviderInfo info = p.info;
336 pw.print(" ["); pw.print(index); pw.print("] provider ");
337 pw.print(info.provider.flattenToShortString());
338 pw.println(':');
339 pw.print(" min=("); pw.print(info.minWidth);
340 pw.print("x"); pw.print(info.minHeight);
341 pw.print(") minResize=("); pw.print(info.minResizeWidth);
342 pw.print("x"); pw.print(info.minResizeHeight);
343 pw.print(") updatePeriodMillis=");
344 pw.print(info.updatePeriodMillis);
345 pw.print(" resizeMode=");
346 pw.print(info.resizeMode);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700347 pw.print(info.widgetCategory);
Amith Yamasani742a6712011-05-04 14:49:28 -0700348 pw.print(" autoAdvanceViewId=");
349 pw.print(info.autoAdvanceViewId);
350 pw.print(" initialLayout=#");
351 pw.print(Integer.toHexString(info.initialLayout));
Amith Yamasani791f8772012-11-21 14:06:07 -0800352 pw.print(" uid="); pw.print(p.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700353 pw.print(" zombie="); pw.println(p.zombie);
354 }
355
356 private void dumpHost(Host host, int index, PrintWriter pw) {
357 pw.print(" ["); pw.print(index); pw.print("] hostId=");
358 pw.print(host.hostId); pw.print(' ');
359 pw.print(host.packageName); pw.print('/');
360 pw.print(host.uid); pw.println(':');
361 pw.print(" callbacks="); pw.println(host.callbacks);
362 pw.print(" instances.size="); pw.print(host.instances.size());
363 pw.print(" zombie="); pw.println(host.zombie);
364 }
365
366 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
367 pw.print(" ["); pw.print(index); pw.print("] id=");
368 pw.println(id.appWidgetId);
369 pw.print(" hostId=");
370 pw.print(id.host.hostId); pw.print(' ');
371 pw.print(id.host.packageName); pw.print('/');
372 pw.println(id.host.uid);
373 if (id.provider != null) {
374 pw.print(" provider=");
375 pw.println(id.provider.info.provider.flattenToShortString());
376 }
377 if (id.host != null) {
378 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
379 }
380 if (id.views != null) {
381 pw.print(" views="); pw.println(id.views);
382 }
383 }
384
385 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
386 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
387 != PackageManager.PERMISSION_GRANTED) {
388 pw.println("Permission Denial: can't dump from from pid="
389 + Binder.getCallingPid()
390 + ", uid=" + Binder.getCallingUid());
391 return;
392 }
393
394 synchronized (mAppWidgetIds) {
395 int N = mInstalledProviders.size();
396 pw.println("Providers:");
397 for (int i=0; i<N; i++) {
398 dumpProvider(mInstalledProviders.get(i), i, pw);
399 }
400
401 N = mAppWidgetIds.size();
402 pw.println(" ");
403 pw.println("AppWidgetIds:");
404 for (int i=0; i<N; i++) {
405 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
406 }
407
408 N = mHosts.size();
409 pw.println(" ");
410 pw.println("Hosts:");
411 for (int i=0; i<N; i++) {
412 dumpHost(mHosts.get(i), i, pw);
413 }
414
415 N = mDeletedProviders.size();
416 pw.println(" ");
417 pw.println("Deleted Providers:");
418 for (int i=0; i<N; i++) {
419 dumpProvider(mDeletedProviders.get(i), i, pw);
420 }
421
422 N = mDeletedHosts.size();
423 pw.println(" ");
424 pw.println("Deleted Hosts:");
425 for (int i=0; i<N; i++) {
426 dumpHost(mDeletedHosts.get(i), i, pw);
427 }
428 }
429 }
430
431 private void ensureStateLoadedLocked() {
432 if (!mStateLoaded) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700433 if (!mHasFeature) {
434 return;
435 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700436 loadAppWidgetListLocked();
Amith Yamasani742a6712011-05-04 14:49:28 -0700437 loadStateLocked();
438 mStateLoaded = true;
439 }
440 }
441
442 public int allocateAppWidgetId(String packageName, int hostId) {
Jim Millerf229e4d2012-09-12 20:32:50 -0700443 int callingUid = enforceSystemOrCallingUid(packageName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700444 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700445 if (!mHasFeature) {
446 return -1;
447 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700448 ensureStateLoadedLocked();
449 int appWidgetId = mNextAppWidgetId++;
450
451 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
452
453 AppWidgetId id = new AppWidgetId();
454 id.appWidgetId = appWidgetId;
455 id.host = host;
456
457 host.instances.add(id);
458 mAppWidgetIds.add(id);
459
Adam Cohena1a2f962012-11-01 14:06:16 -0700460 saveStateAsync();
Amith Yamasani8320de82012-10-05 16:10:38 -0700461 if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
462 + " id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700463 return appWidgetId;
464 }
465 }
466
467 public void deleteAppWidgetId(int appWidgetId) {
468 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700469 if (!mHasFeature) {
470 return;
471 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700472 ensureStateLoadedLocked();
473 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
474 if (id != null) {
475 deleteAppWidgetLocked(id);
Adam Cohena1a2f962012-11-01 14:06:16 -0700476 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700477 }
478 }
479 }
480
481 public void deleteHost(int hostId) {
482 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700483 if (!mHasFeature) {
484 return;
485 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700486 ensureStateLoadedLocked();
487 int callingUid = Binder.getCallingUid();
488 Host host = lookupHostLocked(callingUid, hostId);
489 if (host != null) {
490 deleteHostLocked(host);
Adam Cohena1a2f962012-11-01 14:06:16 -0700491 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700492 }
493 }
494 }
495
496 public void deleteAllHosts() {
497 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700498 if (!mHasFeature) {
499 return;
500 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700501 ensureStateLoadedLocked();
502 int callingUid = Binder.getCallingUid();
503 final int N = mHosts.size();
504 boolean changed = false;
505 for (int i = N - 1; i >= 0; i--) {
506 Host host = mHosts.get(i);
Amith Yamasanic566b432012-11-30 15:26:21 -0800507 if (host.uidMatches(callingUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700508 deleteHostLocked(host);
509 changed = true;
510 }
511 }
512 if (changed) {
Adam Cohena1a2f962012-11-01 14:06:16 -0700513 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700514 }
515 }
516 }
517
518 void deleteHostLocked(Host host) {
519 final int N = host.instances.size();
520 for (int i = N - 1; i >= 0; i--) {
521 AppWidgetId id = host.instances.get(i);
522 deleteAppWidgetLocked(id);
523 }
524 host.instances.clear();
525 mHosts.remove(host);
526 mDeletedHosts.add(host);
527 // it's gone or going away, abruptly drop the callback connection
528 host.callbacks = null;
529 }
530
531 void deleteAppWidgetLocked(AppWidgetId id) {
532 // We first unbind all services that are bound to this id
533 unbindAppWidgetRemoteViewsServicesLocked(id);
534
535 Host host = id.host;
536 host.instances.remove(id);
537 pruneHostLocked(host);
538
539 mAppWidgetIds.remove(id);
540
541 Provider p = id.provider;
542 if (p != null) {
543 p.instances.remove(id);
544 if (!p.zombie) {
545 // send the broacast saying that this appWidgetId has been deleted
546 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
547 intent.setComponent(p.info.provider);
548 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700549 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700550 if (p.instances.size() == 0) {
551 // cancel the future updates
552 cancelBroadcasts(p);
553
554 // send the broacast saying that the provider is not in use any more
555 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
556 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700557 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700558 }
559 }
560 }
561 }
562
563 void cancelBroadcasts(Provider p) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700564 if (DBG) log("cancelBroadcasts for " + p);
Amith Yamasani742a6712011-05-04 14:49:28 -0700565 if (p.broadcast != null) {
566 mAlarmManager.cancel(p.broadcast);
567 long token = Binder.clearCallingIdentity();
568 try {
569 p.broadcast.cancel();
570 } finally {
571 Binder.restoreCallingIdentity(token);
572 }
573 p.broadcast = null;
574 }
575 }
576
Adam Cohen0aa2d422012-09-07 17:37:26 -0700577 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700578 if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
579 + " provider=" + provider);
Amith Yamasani742a6712011-05-04 14:49:28 -0700580 final long ident = Binder.clearCallingIdentity();
581 try {
582 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700583 if (!mHasFeature) {
584 return;
585 }
Adam Cohen3ff2d862012-09-26 14:07:57 -0700586 options = cloneIfLocalBinder(options);
Amith Yamasani742a6712011-05-04 14:49:28 -0700587 ensureStateLoadedLocked();
588 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
589 if (id == null) {
590 throw new IllegalArgumentException("bad appWidgetId");
591 }
592 if (id.provider != null) {
593 throw new IllegalArgumentException("appWidgetId " + appWidgetId
594 + " already bound to " + id.provider.info.provider);
595 }
596 Provider p = lookupProviderLocked(provider);
597 if (p == null) {
598 throw new IllegalArgumentException("not a appwidget provider: " + provider);
599 }
600 if (p.zombie) {
601 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
602 + " safe mode: " + provider);
603 }
604
Amith Yamasani742a6712011-05-04 14:49:28 -0700605 id.provider = p;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700606 if (options == null) {
607 options = new Bundle();
608 }
609 id.options = options;
610
611 // We need to provide a default value for the widget category if it is not specified
612 if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
613 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
614 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
615 }
616
Amith Yamasani742a6712011-05-04 14:49:28 -0700617 p.instances.add(id);
618 int instancesSize = p.instances.size();
619 if (instancesSize == 1) {
620 // tell the provider that it's ready
621 sendEnableIntentLocked(p);
622 }
623
624 // send an update now -- We need this update now, and just for this appWidgetId.
625 // It's less critical when the next one happens, so when we schedule the next one,
626 // we add updatePeriodMillis to its start time. That time will have some slop,
627 // but that's okay.
628 sendUpdateIntentLocked(p, new int[] { appWidgetId });
629
630 // schedule the future updates
631 registerForBroadcastsLocked(p, getAppWidgetIds(p));
Adam Cohena1a2f962012-11-01 14:06:16 -0700632 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700633 }
634 } finally {
635 Binder.restoreCallingIdentity(ident);
636 }
637 }
638
Adam Cohen0aa2d422012-09-07 17:37:26 -0700639 public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka67a871d2012-11-01 18:26:01 -0700640 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET,
Michael Jurka61a5b012012-04-13 10:39:45 -0700641 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700642 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700643 }
644
645 public boolean bindAppWidgetIdIfAllowed(
Adam Cohen0aa2d422012-09-07 17:37:26 -0700646 String packageName, int appWidgetId, ComponentName provider, Bundle options) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700647 if (!mHasFeature) {
648 return false;
649 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700650 try {
Michael Jurka67a871d2012-11-01 18:26:01 -0700651 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
Michael Jurka61a5b012012-04-13 10:39:45 -0700652 } catch (SecurityException se) {
653 if (!callerHasBindAppWidgetPermission(packageName)) {
654 return false;
655 }
656 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700657 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700658 return true;
659 }
660
661 private boolean callerHasBindAppWidgetPermission(String packageName) {
662 int callingUid = Binder.getCallingUid();
663 try {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700664 if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700665 return false;
666 }
667 } catch (Exception e) {
668 return false;
669 }
670 synchronized (mAppWidgetIds) {
671 ensureStateLoadedLocked();
672 return mPackagesWithBindWidgetPermission.contains(packageName);
673 }
674 }
675
676 public boolean hasBindAppWidgetPermission(String packageName) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700677 if (!mHasFeature) {
678 return false;
679 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700680 mContext.enforceCallingPermission(
681 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
682 "hasBindAppWidgetPermission packageName=" + packageName);
683
684 synchronized (mAppWidgetIds) {
685 ensureStateLoadedLocked();
686 return mPackagesWithBindWidgetPermission.contains(packageName);
687 }
688 }
689
690 public void setBindAppWidgetPermission(String packageName, boolean permission) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700691 if (!mHasFeature) {
692 return;
693 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700694 mContext.enforceCallingPermission(
695 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
696 "setBindAppWidgetPermission packageName=" + packageName);
697
698 synchronized (mAppWidgetIds) {
699 ensureStateLoadedLocked();
700 if (permission) {
701 mPackagesWithBindWidgetPermission.add(packageName);
702 } else {
703 mPackagesWithBindWidgetPermission.remove(packageName);
704 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700705 saveStateAsync();
Michael Jurka61a5b012012-04-13 10:39:45 -0700706 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700707 }
708
Amith Yamasani742a6712011-05-04 14:49:28 -0700709 // Binds to a specific RemoteViewsService
710 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
711 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700712 if (!mHasFeature) {
713 return;
714 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700715 ensureStateLoadedLocked();
716 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
717 if (id == null) {
718 throw new IllegalArgumentException("bad appWidgetId");
719 }
720 final ComponentName componentName = intent.getComponent();
721 try {
Amith Yamasani98edc952012-09-25 14:09:27 -0700722 final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
723 PackageManager.GET_PERMISSIONS, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700724 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
725 throw new SecurityException("Selected service does not require "
726 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
727 }
Amith Yamasani98edc952012-09-25 14:09:27 -0700728 } catch (RemoteException e) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700729 throw new IllegalArgumentException("Unknown component " + componentName);
730 }
731
732 // If there is already a connection made for this service intent, then disconnect from
733 // that first. (This does not allow multiple connections to the same service under
734 // the same key)
735 ServiceConnectionProxy conn = null;
736 FilterComparison fc = new FilterComparison(intent);
737 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
738 if (mBoundRemoteViewsServices.containsKey(key)) {
739 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
740 conn.disconnect();
741 mContext.unbindService(conn);
742 mBoundRemoteViewsServices.remove(key);
743 }
744
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700745 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani791f8772012-11-21 14:06:07 -0800746 if (userId != mUserId) {
747 Slog.w(TAG, "AppWidgetServiceImpl of user " + mUserId
748 + " binding to provider on user " + userId);
749 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700750 // Bind to the RemoteViewsService (which will trigger a callback to the
751 // RemoteViewsAdapter.onServiceConnected())
752 final long token = Binder.clearCallingIdentity();
753 try {
754 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani27b89e62013-01-16 12:30:11 -0800755 mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
756 new UserHandle(userId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700757 mBoundRemoteViewsServices.put(key, conn);
758 } finally {
759 Binder.restoreCallingIdentity(token);
760 }
761
762 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
763 // when we can call back to the RemoteViewsService later to destroy associated
764 // factories.
765 incrementAppWidgetServiceRefCount(appWidgetId, fc);
766 }
767 }
768
769 // Unbinds from a specific RemoteViewsService
770 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
771 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700772 if (!mHasFeature) {
773 return;
774 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700775 ensureStateLoadedLocked();
776 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
777 // RemoteViewsAdapter)
778 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
779 intent));
780 if (mBoundRemoteViewsServices.containsKey(key)) {
781 // We don't need to use the appWidgetId until after we are sure there is something
782 // to unbind. Note that this may mask certain issues with apps calling unbind()
783 // more than necessary.
784 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
785 if (id == null) {
786 throw new IllegalArgumentException("bad appWidgetId");
787 }
788
789 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
790 .get(key);
791 conn.disconnect();
792 mContext.unbindService(conn);
793 mBoundRemoteViewsServices.remove(key);
Amith Yamasani742a6712011-05-04 14:49:28 -0700794 }
795 }
796 }
797
798 // Unbinds from a RemoteViewsService when we delete an app widget
799 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
800 int appWidgetId = id.appWidgetId;
801 // Unbind all connections to Services bound to this AppWidgetId
802 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
803 .iterator();
804 while (it.hasNext()) {
805 final Pair<Integer, Intent.FilterComparison> key = it.next();
806 if (key.first.intValue() == appWidgetId) {
807 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
808 .get(key);
809 conn.disconnect();
810 mContext.unbindService(conn);
811 it.remove();
812 }
813 }
814
815 // Check if we need to destroy any services (if no other app widgets are
816 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800817 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700818 }
819
820 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800821 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700822 final ServiceConnection conn = new ServiceConnection() {
823 @Override
824 public void onServiceConnected(ComponentName name, IBinder service) {
825 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
826 try {
827 cb.onDestroy(intent);
828 } catch (RemoteException e) {
829 e.printStackTrace();
830 } catch (RuntimeException e) {
831 e.printStackTrace();
832 }
833 mContext.unbindService(this);
834 }
835
836 @Override
837 public void onServiceDisconnected(android.content.ComponentName name) {
838 // Do nothing
839 }
840 };
841
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700842 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700843 // Bind to the service and remove the static intent->factory mapping in the
844 // RemoteViewsService.
845 final long token = Binder.clearCallingIdentity();
846 try {
Amith Yamasani27b89e62013-01-16 12:30:11 -0800847 mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
848 new UserHandle(userId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700849 } finally {
850 Binder.restoreCallingIdentity(token);
851 }
852 }
853
854 // Adds to the ref-count for a given RemoteViewsService intent
855 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
856 HashSet<Integer> appWidgetIds = null;
857 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
858 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
859 } else {
860 appWidgetIds = new HashSet<Integer>();
861 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
862 }
863 appWidgetIds.add(appWidgetId);
864 }
865
866 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
867 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800868 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700869 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
870 while (it.hasNext()) {
871 final FilterComparison key = it.next();
872 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800873 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700874 // If we have removed the last app widget referencing this service, then we
875 // should destroy it and remove it from this set
876 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800877 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700878 it.remove();
879 }
880 }
881 }
882 }
883
884 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
885 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700886 if (!mHasFeature) {
887 return null;
888 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700889 ensureStateLoadedLocked();
890 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
891 if (id != null && id.provider != null && !id.provider.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700892 return cloneIfLocalBinder(id.provider.info);
Amith Yamasani742a6712011-05-04 14:49:28 -0700893 }
894 return null;
895 }
896 }
897
898 public RemoteViews getAppWidgetViews(int appWidgetId) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700899 if (DBG) log("getAppWidgetViews id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700900 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700901 if (!mHasFeature) {
902 return null;
903 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700904 ensureStateLoadedLocked();
905 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
906 if (id != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700907 return cloneIfLocalBinder(id.views);
Amith Yamasani742a6712011-05-04 14:49:28 -0700908 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700909 if (DBG) log(" couldn't find appwidgetid");
Amith Yamasani742a6712011-05-04 14:49:28 -0700910 return null;
911 }
912 }
913
Adam Cohend9e5af32012-11-28 16:34:57 -0800914 public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700915 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700916 if (!mHasFeature) {
917 return new ArrayList<AppWidgetProviderInfo>(0);
918 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700919 ensureStateLoadedLocked();
920 final int N = mInstalledProviders.size();
921 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
922 for (int i = 0; i < N; i++) {
923 Provider p = mInstalledProviders.get(i);
Adam Cohen3fcc6b22012-11-15 14:10:07 -0800924 if (!p.zombie && (p.info.widgetCategory & categoryFilter) != 0) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700925 result.add(cloneIfLocalBinder(p.info));
Amith Yamasani742a6712011-05-04 14:49:28 -0700926 }
927 }
928 return result;
929 }
930 }
931
932 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700933 if (!mHasFeature) {
934 return;
935 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700936 if (appWidgetIds == null) {
937 return;
938 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700939 if (DBG) log("updateAppWidgetIds views: " + views);
Adam Cohenf08a8b72012-07-16 12:02:10 -0700940 int bitmapMemoryUsage = 0;
941 if (views != null) {
942 bitmapMemoryUsage = views.estimateMemoryUsage();
943 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700944 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
945 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
946 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
947 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
948 " fill the device's screen once.");
949 }
950
Amith Yamasani742a6712011-05-04 14:49:28 -0700951 if (appWidgetIds.length == 0) {
952 return;
953 }
954 final int N = appWidgetIds.length;
955
956 synchronized (mAppWidgetIds) {
957 ensureStateLoadedLocked();
958 for (int i = 0; i < N; i++) {
959 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
960 updateAppWidgetInstanceLocked(id, views);
961 }
962 }
963 }
964
Adam Cohena1a2f962012-11-01 14:06:16 -0700965 private void saveStateAsync() {
966 mSaveStateHandler.post(mSaveStateRunnable);
967 }
968
969 private final Runnable mSaveStateRunnable = new Runnable() {
970 @Override
971 public void run() {
972 synchronized (mAppWidgetIds) {
973 ensureStateLoadedLocked();
974 saveStateLocked();
975 }
976 }
977 };
978
Adam Cohend2097eb2012-05-01 18:10:28 -0700979 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700980 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700981 if (!mHasFeature) {
982 return;
983 }
Adam Cohen3ff2d862012-09-26 14:07:57 -0700984 options = cloneIfLocalBinder(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700985 ensureStateLoadedLocked();
986 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
987
988 if (id == null) {
989 return;
990 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700991
Adam Cohene8724c82012-04-19 17:11:40 -0700992 Provider p = id.provider;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700993 // Merge the options
994 id.options.putAll(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700995
996 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700997 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700998 intent.setComponent(p.info.provider);
999 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001000 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001001 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Adam Cohena1a2f962012-11-01 14:06:16 -07001002 saveStateAsync();
Adam Cohene8724c82012-04-19 17:11:40 -07001003 }
1004 }
1005
Adam Cohend2097eb2012-05-01 18:10:28 -07001006 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -07001007 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001008 if (!mHasFeature) {
1009 return Bundle.EMPTY;
1010 }
Adam Cohene8724c82012-04-19 17:11:40 -07001011 ensureStateLoadedLocked();
1012 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -07001013 if (id != null && id.options != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -07001014 return cloneIfLocalBinder(id.options);
Adam Cohene8724c82012-04-19 17:11:40 -07001015 } else {
1016 return Bundle.EMPTY;
1017 }
1018 }
1019 }
1020
Amith Yamasani742a6712011-05-04 14:49:28 -07001021 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001022 if (!mHasFeature) {
1023 return;
1024 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001025 if (appWidgetIds == null) {
1026 return;
1027 }
1028 if (appWidgetIds.length == 0) {
1029 return;
1030 }
1031 final int N = appWidgetIds.length;
1032
1033 synchronized (mAppWidgetIds) {
1034 ensureStateLoadedLocked();
1035 for (int i = 0; i < N; i++) {
1036 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Amith Yamasani791f8772012-11-21 14:06:07 -08001037 if (id == null) {
Amith Yamasanic566b432012-11-30 15:26:21 -08001038 Slog.w(TAG, "widget id " + appWidgetIds[i] + " not found!");
1039 } else if (id.views != null) {
Winson Chung66119882012-10-11 14:26:25 -07001040 // Only trigger a partial update for a widget if it has received a full update
1041 updateAppWidgetInstanceLocked(id, views, true);
1042 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001043 }
1044 }
1045 }
1046
1047 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001048 if (!mHasFeature) {
1049 return;
1050 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001051 if (appWidgetIds == null) {
1052 return;
1053 }
1054 if (appWidgetIds.length == 0) {
1055 return;
1056 }
1057 final int N = appWidgetIds.length;
1058
1059 synchronized (mAppWidgetIds) {
1060 ensureStateLoadedLocked();
1061 for (int i = 0; i < N; i++) {
1062 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
1063 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
1064 }
1065 }
1066 }
1067
1068 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001069 if (!mHasFeature) {
1070 return;
1071 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001072 synchronized (mAppWidgetIds) {
1073 ensureStateLoadedLocked();
1074 Provider p = lookupProviderLocked(provider);
1075 if (p == null) {
1076 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
1077 return;
1078 }
1079 ArrayList<AppWidgetId> instances = p.instances;
1080 final int callingUid = Binder.getCallingUid();
1081 final int N = instances.size();
1082 for (int i = 0; i < N; i++) {
1083 AppWidgetId id = instances.get(i);
1084 if (canAccessAppWidgetId(id, callingUid)) {
1085 updateAppWidgetInstanceLocked(id, views);
1086 }
1087 }
1088 }
1089 }
1090
1091 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
1092 updateAppWidgetInstanceLocked(id, views, false);
1093 }
1094
1095 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
1096 // allow for stale appWidgetIds and other badness
1097 // lookup also checks that the calling process can access the appWidgetId
1098 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1099 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1100
Winson Chung66119882012-10-11 14:26:25 -07001101 if (!isPartialUpdate) {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001102 // For a full update we replace the RemoteViews completely.
Amith Yamasani742a6712011-05-04 14:49:28 -07001103 id.views = views;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001104 } else {
1105 // For a partial update, we merge the new RemoteViews with the old.
1106 id.views.mergeRemoteViews(views);
1107 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001108
1109 // is anyone listening?
1110 if (id.host.callbacks != null) {
1111 try {
1112 // the lock is held, but this is a oneway call
Jim Millera75a8832013-02-07 16:53:32 -08001113 id.host.callbacks.updateAppWidget(id.appWidgetId, views, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001114 } catch (RemoteException e) {
1115 // It failed; remove the callback. No need to prune because
1116 // we know that this host is still referenced by this instance.
1117 id.host.callbacks = null;
1118 }
1119 }
1120 }
1121 }
1122
1123 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1124 // allow for stale appWidgetIds and other badness
1125 // lookup also checks that the calling process can access the appWidgetId
1126 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1127 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1128 // is anyone listening?
1129 if (id.host.callbacks != null) {
1130 try {
1131 // the lock is held, but this is a oneway call
Jim Millera75a8832013-02-07 16:53:32 -08001132 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001133 } catch (RemoteException e) {
1134 // It failed; remove the callback. No need to prune because
1135 // we know that this host is still referenced by this instance.
1136 id.host.callbacks = null;
1137 }
1138 }
1139
1140 // If the host is unavailable, then we call the associated
1141 // RemoteViewsFactory.onDataSetChanged() directly
1142 if (id.host.callbacks == null) {
1143 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1144 for (FilterComparison key : keys) {
1145 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1146 Intent intent = key.getIntent();
1147
1148 final ServiceConnection conn = new ServiceConnection() {
1149 @Override
1150 public void onServiceConnected(ComponentName name, IBinder service) {
1151 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1152 .asInterface(service);
1153 try {
1154 cb.onDataSetChangedAsync();
1155 } catch (RemoteException e) {
1156 e.printStackTrace();
1157 } catch (RuntimeException e) {
1158 e.printStackTrace();
1159 }
1160 mContext.unbindService(this);
1161 }
1162
1163 @Override
1164 public void onServiceDisconnected(android.content.ComponentName name) {
1165 // Do nothing
1166 }
1167 };
1168
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001169 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001170 // Bind to the service and call onDataSetChanged()
1171 final long token = Binder.clearCallingIdentity();
1172 try {
Amith Yamasani27b89e62013-01-16 12:30:11 -08001173 mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
1174 new UserHandle(userId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001175 } finally {
1176 Binder.restoreCallingIdentity(token);
1177 }
1178 }
1179 }
1180 }
1181 }
1182 }
1183
Adam Cohen3ff2d862012-09-26 14:07:57 -07001184 private boolean isLocalBinder() {
1185 return Process.myPid() == Binder.getCallingPid();
1186 }
1187
1188 private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1189 if (isLocalBinder() && rv != null) {
1190 return rv.clone();
1191 }
1192 return rv;
1193 }
1194
1195 private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
1196 if (isLocalBinder() && info != null) {
1197 return info.clone();
1198 }
1199 return info;
1200 }
1201
1202 private Bundle cloneIfLocalBinder(Bundle bundle) {
1203 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
1204 // if we start adding objects to the options. Further, it would only be an issue if keyguard
1205 // used such options.
1206 if (isLocalBinder() && bundle != null) {
1207 return (Bundle) bundle.clone();
1208 }
1209 return bundle;
1210 }
1211
Amith Yamasani742a6712011-05-04 14:49:28 -07001212 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1213 List<RemoteViews> updatedViews) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001214 if (!mHasFeature) {
1215 return new int[0];
1216 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001217 int callingUid = enforceCallingUid(packageName);
1218 synchronized (mAppWidgetIds) {
1219 ensureStateLoadedLocked();
1220 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1221 host.callbacks = callbacks;
1222
1223 updatedViews.clear();
1224
1225 ArrayList<AppWidgetId> instances = host.instances;
1226 int N = instances.size();
1227 int[] updatedIds = new int[N];
1228 for (int i = 0; i < N; i++) {
1229 AppWidgetId id = instances.get(i);
1230 updatedIds[i] = id.appWidgetId;
Adam Cohen3ff2d862012-09-26 14:07:57 -07001231 updatedViews.add(cloneIfLocalBinder(id.views));
Amith Yamasani742a6712011-05-04 14:49:28 -07001232 }
1233 return updatedIds;
1234 }
1235 }
1236
1237 public void stopListening(int hostId) {
1238 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001239 if (!mHasFeature) {
1240 return;
1241 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001242 ensureStateLoadedLocked();
1243 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1244 if (host != null) {
1245 host.callbacks = null;
1246 pruneHostLocked(host);
1247 }
1248 }
1249 }
1250
1251 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
Amith Yamasanic566b432012-11-30 15:26:21 -08001252 if (id.host.uidMatches(callingUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001253 // Apps hosting the AppWidget have access to it.
1254 return true;
1255 }
1256 if (id.provider != null && id.provider.uid == callingUid) {
1257 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1258 return true;
1259 }
1260 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1261 // Apps that can bind have access to all appWidgetIds.
1262 return true;
1263 }
1264 // Nobody else can access it.
1265 return false;
1266 }
1267
1268 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1269 int callingUid = Binder.getCallingUid();
1270 final int N = mAppWidgetIds.size();
1271 for (int i = 0; i < N; i++) {
1272 AppWidgetId id = mAppWidgetIds.get(i);
1273 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1274 return id;
1275 }
1276 }
1277 return null;
1278 }
1279
1280 Provider lookupProviderLocked(ComponentName provider) {
1281 final int N = mInstalledProviders.size();
1282 for (int i = 0; i < N; i++) {
1283 Provider p = mInstalledProviders.get(i);
1284 if (p.info.provider.equals(provider)) {
1285 return p;
1286 }
1287 }
1288 return null;
1289 }
1290
1291 Host lookupHostLocked(int uid, int hostId) {
1292 final int N = mHosts.size();
1293 for (int i = 0; i < N; i++) {
1294 Host h = mHosts.get(i);
Amith Yamasanic566b432012-11-30 15:26:21 -08001295 if (h.uidMatches(uid) && h.hostId == hostId) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001296 return h;
1297 }
1298 }
1299 return null;
1300 }
1301
1302 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1303 final int N = mHosts.size();
1304 for (int i = 0; i < N; i++) {
1305 Host h = mHosts.get(i);
1306 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1307 return h;
1308 }
1309 }
1310 Host host = new Host();
1311 host.packageName = packageName;
1312 host.uid = uid;
1313 host.hostId = hostId;
1314 mHosts.add(host);
1315 return host;
1316 }
1317
1318 void pruneHostLocked(Host host) {
1319 if (host.instances.size() == 0 && host.callbacks == null) {
1320 mHosts.remove(host);
1321 }
1322 }
1323
Adam Cohena1a2f962012-11-01 14:06:16 -07001324 void loadAppWidgetListLocked() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001325 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001326 try {
1327 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1328 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1329 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001330
Amith Yamasani483f3b02012-03-13 16:08:00 -07001331 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1332 for (int i = 0; i < N; i++) {
1333 ResolveInfo ri = broadcastReceivers.get(i);
1334 addProviderLocked(ri);
1335 }
1336 } catch (RemoteException re) {
1337 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001338 }
1339 }
1340
1341 boolean addProviderLocked(ResolveInfo ri) {
1342 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1343 return false;
1344 }
1345 if (!ri.activityInfo.isEnabled()) {
1346 return false;
1347 }
1348 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1349 ri.activityInfo.name), ri);
1350 if (p != null) {
1351 mInstalledProviders.add(p);
1352 return true;
1353 } else {
1354 return false;
1355 }
1356 }
1357
1358 void removeProviderLocked(int index, Provider p) {
1359 int N = p.instances.size();
1360 for (int i = 0; i < N; i++) {
1361 AppWidgetId id = p.instances.get(i);
1362 // Call back with empty RemoteViews
1363 updateAppWidgetInstanceLocked(id, null);
1364 // Stop telling the host about updates for this from now on
1365 cancelBroadcasts(p);
1366 // clear out references to this appWidgetId
1367 id.host.instances.remove(id);
1368 mAppWidgetIds.remove(id);
1369 id.provider = null;
1370 pruneHostLocked(id.host);
1371 id.host = null;
1372 }
1373 p.instances.clear();
1374 mInstalledProviders.remove(index);
1375 mDeletedProviders.add(p);
1376 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1377 cancelBroadcasts(p);
1378 }
1379
1380 void sendEnableIntentLocked(Provider p) {
1381 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1382 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001383 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001384 }
1385
1386 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1387 if (appWidgetIds != null && appWidgetIds.length > 0) {
1388 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1389 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1390 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001391 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001392 }
1393 }
1394
1395 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1396 if (p.info.updatePeriodMillis > 0) {
1397 // if this is the first instance, set the alarm. otherwise,
1398 // rely on the fact that we've already set it and that
1399 // PendingIntent.getBroadcast will update the extras.
1400 boolean alreadyRegistered = p.broadcast != null;
1401 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1402 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1403 intent.setComponent(p.info.provider);
1404 long token = Binder.clearCallingIdentity();
1405 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001406 p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
1407 PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001408 } finally {
1409 Binder.restoreCallingIdentity(token);
1410 }
1411 if (!alreadyRegistered) {
1412 long period = p.info.updatePeriodMillis;
1413 if (period < MIN_UPDATE_PERIOD) {
1414 period = MIN_UPDATE_PERIOD;
1415 }
1416 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1417 .elapsedRealtime()
1418 + period, period, p.broadcast);
1419 }
1420 }
1421 }
1422
1423 static int[] getAppWidgetIds(Provider p) {
1424 int instancesSize = p.instances.size();
1425 int appWidgetIds[] = new int[instancesSize];
1426 for (int i = 0; i < instancesSize; i++) {
1427 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1428 }
1429 return appWidgetIds;
1430 }
1431
1432 public int[] getAppWidgetIds(ComponentName provider) {
1433 synchronized (mAppWidgetIds) {
1434 ensureStateLoadedLocked();
1435 Provider p = lookupProviderLocked(provider);
1436 if (p != null && Binder.getCallingUid() == p.uid) {
1437 return getAppWidgetIds(p);
1438 } else {
1439 return new int[0];
1440 }
1441 }
1442 }
1443
Michael Jurka75b5cfb2012-11-15 18:22:47 -08001444 static int[] getAppWidgetIds(Host h) {
1445 int instancesSize = h.instances.size();
1446 int appWidgetIds[] = new int[instancesSize];
1447 for (int i = 0; i < instancesSize; i++) {
1448 appWidgetIds[i] = h.instances.get(i).appWidgetId;
1449 }
1450 return appWidgetIds;
1451 }
1452
1453 public int[] getAppWidgetIdsForHost(int hostId) {
1454 synchronized (mAppWidgetIds) {
1455 ensureStateLoadedLocked();
1456 int callingUid = Binder.getCallingUid();
1457 Host host = lookupHostLocked(callingUid, hostId);
1458 if (host != null) {
1459 return getAppWidgetIds(host);
1460 } else {
1461 return new int[0];
1462 }
1463 }
1464 }
1465
Amith Yamasani742a6712011-05-04 14:49:28 -07001466 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1467 Provider p = null;
1468
1469 ActivityInfo activityInfo = ri.activityInfo;
1470 XmlResourceParser parser = null;
1471 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001472 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001473 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1474 if (parser == null) {
1475 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1476 + " meta-data for " + "AppWidget provider '" + component + '\'');
1477 return null;
1478 }
1479
1480 AttributeSet attrs = Xml.asAttributeSet(parser);
1481
1482 int type;
1483 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1484 && type != XmlPullParser.START_TAG) {
1485 // drain whitespace, comments, etc.
1486 }
1487
1488 String nodeName = parser.getName();
1489 if (!"appwidget-provider".equals(nodeName)) {
1490 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1491 + " AppWidget provider '" + component + '\'');
1492 return null;
1493 }
1494
1495 p = new Provider();
1496 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1497 info.provider = component;
1498 p.uid = activityInfo.applicationInfo.uid;
1499
Amith Yamasani483f3b02012-03-13 16:08:00 -07001500 Resources res = mContext.getPackageManager()
Amith Yamasani8320de82012-10-05 16:10:38 -07001501 .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001502
1503 TypedArray sa = res.obtainAttributes(attrs,
1504 com.android.internal.R.styleable.AppWidgetProviderInfo);
1505
1506 // These dimensions has to be resolved in the application's context.
1507 // We simply send back the raw complex data, which will be
1508 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1509 TypedValue value = sa
1510 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1511 info.minWidth = value != null ? value.data : 0;
1512 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1513 info.minHeight = value != null ? value.data : 0;
1514 value = sa.peekValue(
1515 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1516 info.minResizeWidth = value != null ? value.data : info.minWidth;
1517 value = sa.peekValue(
1518 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1519 info.minResizeHeight = value != null ? value.data : info.minHeight;
1520 info.updatePeriodMillis = sa.getInt(
1521 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1522 info.initialLayout = sa.getResourceId(
1523 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001524 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1525 AppWidgetProviderInfo_initialKeyguardLayout, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001526 String className = sa
1527 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1528 if (className != null) {
1529 info.configure = new ComponentName(component.getPackageName(), className);
1530 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001531 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001532 info.icon = ri.getIconResource();
1533 info.previewImage = sa.getResourceId(
1534 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1535 info.autoAdvanceViewId = sa.getResourceId(
1536 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1537 info.resizeMode = sa.getInt(
1538 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1539 AppWidgetProviderInfo.RESIZE_NONE);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001540 info.widgetCategory = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001541 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001542 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
Amith Yamasani742a6712011-05-04 14:49:28 -07001543
1544 sa.recycle();
1545 } catch (Exception e) {
1546 // Ok to catch Exception here, because anything going wrong because
1547 // of what a client process passes to us should not be fatal for the
1548 // system process.
1549 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1550 return null;
1551 } finally {
1552 if (parser != null)
1553 parser.close();
1554 }
1555 return p;
1556 }
1557
1558 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001559 PackageInfo pkgInfo = null;
1560 try {
1561 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1562 } catch (RemoteException re) {
1563 // Shouldn't happen, local call
1564 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001565 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1566 throw new PackageManager.NameNotFoundException();
1567 }
1568 return pkgInfo.applicationInfo.uid;
1569 }
1570
Jim Millerf229e4d2012-09-12 20:32:50 -07001571 int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1572 int callingUid = Binder.getCallingUid();
Michael Jurka03bdc8a2012-09-21 16:10:21 -07001573 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
Jim Millerf229e4d2012-09-12 20:32:50 -07001574 return callingUid;
1575 }
1576 return enforceCallingUid(packageName);
1577 }
1578
Amith Yamasani742a6712011-05-04 14:49:28 -07001579 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1580 int callingUid = Binder.getCallingUid();
1581 int packageUid;
1582 try {
1583 packageUid = getUidForPackage(packageName);
1584 } catch (PackageManager.NameNotFoundException ex) {
1585 throw new IllegalArgumentException("packageName and uid don't match packageName="
1586 + packageName);
1587 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001588 if (!UserHandle.isSameApp(callingUid, packageUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001589 throw new IllegalArgumentException("packageName and uid don't match packageName="
1590 + packageName);
1591 }
1592 return callingUid;
1593 }
1594
1595 void sendInitialBroadcasts() {
1596 synchronized (mAppWidgetIds) {
1597 ensureStateLoadedLocked();
1598 final int N = mInstalledProviders.size();
1599 for (int i = 0; i < N; i++) {
1600 Provider p = mInstalledProviders.get(i);
1601 if (p.instances.size() > 0) {
1602 sendEnableIntentLocked(p);
1603 int[] appWidgetIds = getAppWidgetIds(p);
1604 sendUpdateIntentLocked(p, appWidgetIds);
1605 registerForBroadcastsLocked(p, appWidgetIds);
1606 }
1607 }
1608 }
1609 }
1610
1611 // only call from initialization -- it assumes that the data structures are all empty
1612 void loadStateLocked() {
1613 AtomicFile file = savedStateFile();
1614 try {
1615 FileInputStream stream = file.openRead();
1616 readStateFromFileLocked(stream);
1617
1618 if (stream != null) {
1619 try {
1620 stream.close();
1621 } catch (IOException e) {
1622 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1623 }
1624 }
1625 } catch (FileNotFoundException e) {
1626 Slog.w(TAG, "Failed to read state: " + e);
1627 }
1628 }
1629
1630 void saveStateLocked() {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001631 if (!mHasFeature) {
1632 return;
1633 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001634 AtomicFile file = savedStateFile();
1635 FileOutputStream stream;
1636 try {
1637 stream = file.startWrite();
1638 if (writeStateToFileLocked(stream)) {
1639 file.finishWrite(stream);
1640 } else {
1641 file.failWrite(stream);
1642 Slog.w(TAG, "Failed to save state, restoring backup.");
1643 }
1644 } catch (IOException e) {
1645 Slog.w(TAG, "Failed open state file for write: " + e);
1646 }
1647 }
1648
1649 boolean writeStateToFileLocked(FileOutputStream stream) {
1650 int N;
1651
1652 try {
1653 XmlSerializer out = new FastXmlSerializer();
1654 out.setOutput(stream, "utf-8");
1655 out.startDocument(null, true);
1656 out.startTag(null, "gs");
1657
1658 int providerIndex = 0;
1659 N = mInstalledProviders.size();
1660 for (int i = 0; i < N; i++) {
1661 Provider p = mInstalledProviders.get(i);
1662 if (p.instances.size() > 0) {
1663 out.startTag(null, "p");
1664 out.attribute(null, "pkg", p.info.provider.getPackageName());
1665 out.attribute(null, "cl", p.info.provider.getClassName());
1666 out.endTag(null, "p");
1667 p.tag = providerIndex;
1668 providerIndex++;
1669 }
1670 }
1671
1672 N = mHosts.size();
1673 for (int i = 0; i < N; i++) {
1674 Host host = mHosts.get(i);
1675 out.startTag(null, "h");
1676 out.attribute(null, "pkg", host.packageName);
1677 out.attribute(null, "id", Integer.toHexString(host.hostId));
1678 out.endTag(null, "h");
1679 host.tag = i;
1680 }
1681
1682 N = mAppWidgetIds.size();
1683 for (int i = 0; i < N; i++) {
1684 AppWidgetId id = mAppWidgetIds.get(i);
1685 out.startTag(null, "g");
1686 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1687 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1688 if (id.provider != null) {
1689 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1690 }
Adam Cohen0aa2d422012-09-07 17:37:26 -07001691 if (id.options != null) {
1692 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1693 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1694 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1695 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1696 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1697 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1698 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1699 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1700 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1701 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1702 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001703 out.endTag(null, "g");
1704 }
1705
Michael Jurka61a5b012012-04-13 10:39:45 -07001706 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1707 while (it.hasNext()) {
1708 out.startTag(null, "b");
1709 out.attribute(null, "packageName", it.next());
1710 out.endTag(null, "b");
1711 }
1712
Amith Yamasani742a6712011-05-04 14:49:28 -07001713 out.endTag(null, "gs");
1714
1715 out.endDocument();
1716 return true;
1717 } catch (IOException e) {
1718 Slog.w(TAG, "Failed to write state: " + e);
1719 return false;
1720 }
1721 }
1722
Adam Cohen0aa2d422012-09-07 17:37:26 -07001723 @SuppressWarnings("unused")
Amith Yamasani742a6712011-05-04 14:49:28 -07001724 void readStateFromFileLocked(FileInputStream stream) {
1725 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001726 try {
1727 XmlPullParser parser = Xml.newPullParser();
1728 parser.setInput(stream, null);
1729
1730 int type;
1731 int providerIndex = 0;
1732 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1733 do {
1734 type = parser.next();
1735 if (type == XmlPullParser.START_TAG) {
1736 String tag = parser.getName();
1737 if ("p".equals(tag)) {
1738 // TODO: do we need to check that this package has the same signature
1739 // as before?
1740 String pkg = parser.getAttributeValue(null, "pkg");
1741 String cl = parser.getAttributeValue(null, "cl");
1742
Amith Yamasanif203aee2012-08-29 18:41:53 -07001743 final IPackageManager packageManager = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -07001744 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001745 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
Amith Yamasanif203aee2012-08-29 18:41:53 -07001746 } catch (RemoteException e) {
1747 String[] pkgs = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001748 .currentToCanonicalPackageNames(new String[] { pkg });
1749 pkg = pkgs[0];
1750 }
1751
1752 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1753 if (p == null && mSafeMode) {
1754 // if we're in safe mode, make a temporary one
1755 p = new Provider();
1756 p.info = new AppWidgetProviderInfo();
1757 p.info.provider = new ComponentName(pkg, cl);
1758 p.zombie = true;
1759 mInstalledProviders.add(p);
1760 }
1761 if (p != null) {
1762 // if it wasn't uninstalled or something
1763 loadedProviders.put(providerIndex, p);
1764 }
1765 providerIndex++;
1766 } else if ("h".equals(tag)) {
1767 Host host = new Host();
1768
1769 // TODO: do we need to check that this package has the same signature
1770 // as before?
1771 host.packageName = parser.getAttributeValue(null, "pkg");
1772 try {
1773 host.uid = getUidForPackage(host.packageName);
1774 } catch (PackageManager.NameNotFoundException ex) {
1775 host.zombie = true;
1776 }
1777 if (!host.zombie || mSafeMode) {
1778 // In safe mode, we don't discard the hosts we don't recognize
1779 // so that they're not pruned from our list. Otherwise, we do.
1780 host.hostId = Integer
1781 .parseInt(parser.getAttributeValue(null, "id"), 16);
1782 mHosts.add(host);
1783 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001784 } else if ("b".equals(tag)) {
1785 String packageName = parser.getAttributeValue(null, "packageName");
1786 if (packageName != null) {
1787 mPackagesWithBindWidgetPermission.add(packageName);
1788 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001789 } else if ("g".equals(tag)) {
1790 AppWidgetId id = new AppWidgetId();
1791 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1792 if (id.appWidgetId >= mNextAppWidgetId) {
1793 mNextAppWidgetId = id.appWidgetId + 1;
1794 }
1795
Adam Cohen0aa2d422012-09-07 17:37:26 -07001796 Bundle options = new Bundle();
1797 String minWidthString = parser.getAttributeValue(null, "min_width");
1798 if (minWidthString != null) {
1799 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1800 Integer.parseInt(minWidthString, 16));
1801 }
1802 String minHeightString = parser.getAttributeValue(null, "min_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001803 if (minHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001804 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1805 Integer.parseInt(minHeightString, 16));
1806 }
Adam Cohendb38d8a2012-09-21 18:14:58 -07001807 String maxWidthString = parser.getAttributeValue(null, "max_width");
1808 if (maxWidthString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001809 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1810 Integer.parseInt(maxWidthString, 16));
1811 }
1812 String maxHeightString = parser.getAttributeValue(null, "max_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001813 if (maxHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001814 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1815 Integer.parseInt(maxHeightString, 16));
1816 }
1817 String categoryString = parser.getAttributeValue(null, "host_category");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001818 if (categoryString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001819 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1820 Integer.parseInt(categoryString, 16));
1821 }
1822 id.options = options;
1823
Amith Yamasani742a6712011-05-04 14:49:28 -07001824 String providerString = parser.getAttributeValue(null, "p");
1825 if (providerString != null) {
1826 // there's no provider if it hasn't been bound yet.
1827 // maybe we don't have to save this, but it brings the system
1828 // to the state it was in.
1829 int pIndex = Integer.parseInt(providerString, 16);
1830 id.provider = loadedProviders.get(pIndex);
1831 if (false) {
1832 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1833 + pIndex + " which is " + id.provider);
1834 }
1835 if (id.provider == null) {
1836 // This provider is gone. We just let the host figure out
1837 // that this happened when it fails to load it.
1838 continue;
1839 }
1840 }
1841
1842 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1843 id.host = mHosts.get(hIndex);
1844 if (id.host == null) {
1845 // This host is gone.
1846 continue;
1847 }
1848
1849 if (id.provider != null) {
1850 id.provider.instances.add(id);
1851 }
1852 id.host.instances.add(id);
1853 mAppWidgetIds.add(id);
1854 }
1855 }
1856 } while (type != XmlPullParser.END_DOCUMENT);
1857 success = true;
1858 } catch (NullPointerException e) {
1859 Slog.w(TAG, "failed parsing " + e);
1860 } catch (NumberFormatException e) {
1861 Slog.w(TAG, "failed parsing " + e);
1862 } catch (XmlPullParserException e) {
1863 Slog.w(TAG, "failed parsing " + e);
1864 } catch (IOException e) {
1865 Slog.w(TAG, "failed parsing " + e);
1866 } catch (IndexOutOfBoundsException e) {
1867 Slog.w(TAG, "failed parsing " + e);
1868 }
1869
1870 if (success) {
1871 // delete any hosts that didn't manage to get connected (should happen)
1872 // if it matters, they'll be reconnected.
1873 for (int i = mHosts.size() - 1; i >= 0; i--) {
1874 pruneHostLocked(mHosts.get(i));
1875 }
1876 } else {
1877 // failed reading, clean up
1878 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1879
1880 mAppWidgetIds.clear();
1881 mHosts.clear();
1882 final int N = mInstalledProviders.size();
1883 for (int i = 0; i < N; i++) {
1884 mInstalledProviders.get(i).instances.clear();
1885 }
1886 }
1887 }
1888
Amith Yamasani13593602012-03-22 16:16:17 -07001889 static File getSettingsFile(int userId) {
Amith Yamasani61f57372012-08-31 12:12:28 -07001890 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
Amith Yamasani13593602012-03-22 16:16:17 -07001891 }
1892
Amith Yamasani742a6712011-05-04 14:49:28 -07001893 AtomicFile savedStateFile() {
Amith Yamasani61f57372012-08-31 12:12:28 -07001894 File dir = Environment.getUserSystemDirectory(mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001895 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001896 if (!settingsFile.exists() && mUserId == 0) {
1897 if (!dir.exists()) {
1898 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001899 }
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001900 // Migrate old data
1901 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1902 // Method doesn't throw an exception on failure. Ignore any errors
1903 // in moving the file (like non-existence)
1904 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001905 }
1906 return new AtomicFile(settingsFile);
1907 }
1908
Amith Yamasani756901d2012-10-12 12:30:07 -07001909 void onUserStopping() {
Amith Yamasani13593602012-03-22 16:16:17 -07001910 // prune the ones we don't want to keep
1911 int N = mInstalledProviders.size();
1912 for (int i = N - 1; i >= 0; i--) {
1913 Provider p = mInstalledProviders.get(i);
1914 cancelBroadcasts(p);
1915 }
Amith Yamasani756901d2012-10-12 12:30:07 -07001916 }
1917
1918 void onUserRemoved() {
Amith Yamasani13593602012-03-22 16:16:17 -07001919 getSettingsFile(mUserId).delete();
1920 }
1921
Winson Chung7fbd2842012-06-13 10:35:51 -07001922 boolean addProvidersForPackageLocked(String pkgName) {
1923 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001924 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1925 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001926 List<ResolveInfo> broadcastReceivers;
1927 try {
1928 broadcastReceivers = mPm.queryIntentReceivers(intent,
1929 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1930 PackageManager.GET_META_DATA, mUserId);
1931 } catch (RemoteException re) {
1932 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001933 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001934 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001935 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1936 for (int i = 0; i < N; i++) {
1937 ResolveInfo ri = broadcastReceivers.get(i);
1938 ActivityInfo ai = ri.activityInfo;
1939 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1940 continue;
1941 }
1942 if (pkgName.equals(ai.packageName)) {
1943 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001944 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001945 }
1946 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001947
1948 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001949 }
1950
Winson Chunga3195052012-06-25 10:02:10 -07001951 /**
1952 * Updates all providers with the specified package names, and records any providers that were
1953 * pruned.
1954 *
1955 * @return whether any providers were updated
1956 */
1957 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001958 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001959 HashSet<String> keep = new HashSet<String>();
1960 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1961 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001962 List<ResolveInfo> broadcastReceivers;
1963 try {
1964 broadcastReceivers = mPm.queryIntentReceivers(intent,
1965 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1966 PackageManager.GET_META_DATA, mUserId);
1967 } catch (RemoteException re) {
1968 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001969 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001970 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001971
1972 // add the missing ones and collect which ones to keep
1973 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1974 for (int i = 0; i < N; i++) {
1975 ResolveInfo ri = broadcastReceivers.get(i);
1976 ActivityInfo ai = ri.activityInfo;
1977 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1978 continue;
1979 }
1980 if (pkgName.equals(ai.packageName)) {
1981 ComponentName component = new ComponentName(ai.packageName, ai.name);
1982 Provider p = lookupProviderLocked(component);
1983 if (p == null) {
1984 if (addProviderLocked(ri)) {
1985 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001986 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001987 }
1988 } else {
1989 Provider parsed = parseProviderInfoXml(component, ri);
1990 if (parsed != null) {
1991 keep.add(ai.name);
1992 // Use the new AppWidgetProviderInfo.
1993 p.info = parsed.info;
1994 // If it's enabled
1995 final int M = p.instances.size();
1996 if (M > 0) {
1997 int[] appWidgetIds = getAppWidgetIds(p);
1998 // Reschedule for the new updatePeriodMillis (don't worry about handling
1999 // it specially if updatePeriodMillis didn't change because we just sent
2000 // an update, and the next one will be updatePeriodMillis from now).
2001 cancelBroadcasts(p);
2002 registerForBroadcastsLocked(p, appWidgetIds);
2003 // If it's currently showing, call back with the new
2004 // AppWidgetProviderInfo.
2005 for (int j = 0; j < M; j++) {
2006 AppWidgetId id = p.instances.get(j);
2007 id.views = null;
2008 if (id.host != null && id.host.callbacks != null) {
2009 try {
Jim Millera75a8832013-02-07 16:53:32 -08002010 id.host.callbacks.providerChanged(id.appWidgetId, p.info,
2011 mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07002012 } catch (RemoteException ex) {
2013 // It failed; remove the callback. No need to prune because
2014 // we know that this host is still referenced by this
2015 // instance.
2016 id.host.callbacks = null;
2017 }
2018 }
2019 }
2020 // Now that we've told the host, push out an update.
2021 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07002022 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07002023 }
2024 }
2025 }
2026 }
2027 }
2028
2029 // prune the ones we don't want to keep
2030 N = mInstalledProviders.size();
2031 for (int i = N - 1; i >= 0; i--) {
2032 Provider p = mInstalledProviders.get(i);
2033 if (pkgName.equals(p.info.provider.getPackageName())
2034 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07002035 if (removedProviders != null) {
2036 removedProviders.add(p.info.provider);
2037 }
Amith Yamasani742a6712011-05-04 14:49:28 -07002038 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07002039 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07002040 }
2041 }
Winson Chung7fbd2842012-06-13 10:35:51 -07002042
2043 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07002044 }
2045
Winson Chung7fbd2842012-06-13 10:35:51 -07002046 boolean removeProvidersForPackageLocked(String pkgName) {
2047 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07002048 int N = mInstalledProviders.size();
2049 for (int i = N - 1; i >= 0; i--) {
2050 Provider p = mInstalledProviders.get(i);
2051 if (pkgName.equals(p.info.provider.getPackageName())) {
2052 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07002053 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07002054 }
2055 }
2056
2057 // Delete the hosts for this package too
2058 //
2059 // By now, we have removed any AppWidgets that were in any hosts here,
2060 // so we don't need to worry about sending DISABLE broadcasts to them.
2061 N = mHosts.size();
2062 for (int i = N - 1; i >= 0; i--) {
2063 Host host = mHosts.get(i);
2064 if (pkgName.equals(host.packageName)) {
2065 deleteHostLocked(host);
2066 }
2067 }
Winson Chung7fbd2842012-06-13 10:35:51 -07002068
2069 return providersRemoved;
2070 }
2071
2072 void notifyHostsForProvidersChangedLocked() {
2073 final int N = mHosts.size();
2074 for (int i = N - 1; i >= 0; i--) {
2075 Host host = mHosts.get(i);
2076 try {
2077 if (host.callbacks != null) {
Jim Millera75a8832013-02-07 16:53:32 -08002078 host.callbacks.providersChanged(mUserId);
Winson Chung7fbd2842012-06-13 10:35:51 -07002079 }
2080 } catch (RemoteException ex) {
2081 // It failed; remove the callback. No need to prune because
2082 // we know that this host is still referenced by this
2083 // instance.
2084 host.callbacks = null;
2085 }
2086 }
Amith Yamasani742a6712011-05-04 14:49:28 -07002087 }
2088}