blob: 69ae846597f7bee4ad44cfb5a3ec69caf07b99ef [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
Jim Miller39d129e2013-03-07 16:28:24 -080089 private static final String KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
90 private static final int KEYGUARD_HOST_ID = 0x4b455947;
Amith Yamasani742a6712011-05-04 14:49:28 -070091 private static final String TAG = "AppWidgetServiceImpl";
92 private static final String SETTINGS_FILENAME = "appwidgets.xml";
93 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
Jim Miller39d129e2013-03-07 16:28:24 -080094 private static final int CURRENT_VERSION = 1; // Bump if the stored widgets need to be upgraded.
Amith Yamasani742a6712011-05-04 14:49:28 -070095
Amith Yamasani8320de82012-10-05 16:10:38 -070096 private static boolean DBG = false;
97
Amith Yamasani742a6712011-05-04 14:49:28 -070098 /*
99 * When identifying a Host or Provider based on the calling process, use the uid field. When
100 * identifying a Host or Provider based on a package manager broadcast, use the package given.
101 */
102
103 static class Provider {
104 int uid;
105 AppWidgetProviderInfo info;
106 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
107 PendingIntent broadcast;
108 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
109
110 int tag; // for use while saving state (the index)
111 }
112
113 static class Host {
114 int uid;
115 int hostId;
116 String packageName;
117 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
118 IAppWidgetHost callbacks;
119 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
120
121 int tag; // for use while saving state (the index)
Amith Yamasanic566b432012-11-30 15:26:21 -0800122
123 boolean uidMatches(int callingUid) {
124 if (UserHandle.getAppId(callingUid) == Process.myUid()) {
125 // For a host that's in the system process, ignore the user id
126 return UserHandle.isSameApp(this.uid, callingUid);
127 } else {
128 return this.uid == callingUid;
129 }
130 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700131 }
132
133 static class AppWidgetId {
134 int appWidgetId;
135 Provider provider;
136 RemoteViews views;
Adam Cohend2097eb2012-05-01 18:10:28 -0700137 Bundle options;
Amith Yamasani742a6712011-05-04 14:49:28 -0700138 Host host;
139 }
140
141 /**
142 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
143 * needs to be a static inner class since a reference to the ServiceConnection is held globally
144 * and may lead us to leak AppWidgetService instances (if there were more than one).
145 */
146 static class ServiceConnectionProxy implements ServiceConnection {
147 private final IBinder mConnectionCb;
148
149 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
150 mConnectionCb = connectionCb;
151 }
152
153 public void onServiceConnected(ComponentName name, IBinder service) {
154 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
155 .asInterface(mConnectionCb);
156 try {
157 cb.onServiceConnected(service);
158 } catch (Exception e) {
159 e.printStackTrace();
160 }
161 }
162
163 public void onServiceDisconnected(ComponentName name) {
164 disconnect();
165 }
166
167 public void disconnect() {
168 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
169 .asInterface(mConnectionCb);
170 try {
171 cb.onServiceDisconnected();
172 } catch (Exception e) {
173 e.printStackTrace();
174 }
175 }
176 }
177
178 // Manages active connections to RemoteViewsServices
179 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
180 // Manages persistent references to RemoteViewsServices from different App Widgets
181 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
182
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700183 final Context mContext;
184 final IPackageManager mPm;
185 final AlarmManager mAlarmManager;
186 final ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
187 final int mUserId;
188 final boolean mHasFeature;
189
Amith Yamasani742a6712011-05-04 14:49:28 -0700190 Locale mLocale;
Amith Yamasani742a6712011-05-04 14:49:28 -0700191 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
192 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700193 final ArrayList<Host> mHosts = new ArrayList<Host>();
Michael Jurka61a5b012012-04-13 10:39:45 -0700194 // set of package names
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700195 final HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
Amith Yamasani742a6712011-05-04 14:49:28 -0700196 boolean mSafeMode;
Amith Yamasani742a6712011-05-04 14:49:28 -0700197 boolean mStateLoaded;
Adam Cohen311c79c2012-05-10 14:44:38 -0700198 int mMaxWidgetBitmapMemory;
Amith Yamasani742a6712011-05-04 14:49:28 -0700199
Adam Cohena1a2f962012-11-01 14:06:16 -0700200 private final Handler mSaveStateHandler;
201
Amith Yamasani742a6712011-05-04 14:49:28 -0700202 // These are for debugging only -- widgets are going missing in some rare instances
203 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
204 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
205
Adam Cohena1a2f962012-11-01 14:06:16 -0700206 AppWidgetServiceImpl(Context context, int userId, Handler saveStateHandler) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700207 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700208 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700209 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
210 mUserId = userId;
Adam Cohena1a2f962012-11-01 14:06:16 -0700211 mSaveStateHandler = saveStateHandler;
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700212 mHasFeature = context.getPackageManager().hasSystemFeature(
213 PackageManager.FEATURE_APP_WIDGETS);
Adam Cohen311c79c2012-05-10 14:44:38 -0700214 computeMaximumWidgetBitmapMemory();
215 }
216
217 void computeMaximumWidgetBitmapMemory() {
218 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Jeff Browna8b9def2012-07-23 14:22:49 -0700219 Display display = wm.getDefaultDisplay();
220 Point size = new Point();
221 display.getRealSize(size);
Winson Chunge92aad42012-06-22 14:11:47 -0700222 // Cap memory usage at 1.5 times the size of the display
223 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
Jeff Browna8b9def2012-07-23 14:22:49 -0700224 mMaxWidgetBitmapMemory = 6 * size.x * size.y;
Amith Yamasani742a6712011-05-04 14:49:28 -0700225 }
226
227 public void systemReady(boolean safeMode) {
228 mSafeMode = safeMode;
229
230 synchronized (mAppWidgetIds) {
231 ensureStateLoadedLocked();
232 }
233 }
234
Amith Yamasani8320de82012-10-05 16:10:38 -0700235 private void log(String msg) {
236 Slog.i(TAG, "u=" + mUserId + ": " + msg);
237 }
238
Amith Yamasani742a6712011-05-04 14:49:28 -0700239 void onConfigurationChanged() {
Amith Yamasani8320de82012-10-05 16:10:38 -0700240 if (DBG) log("Got onConfigurationChanged()");
Amith Yamasani742a6712011-05-04 14:49:28 -0700241 Locale revised = Locale.getDefault();
242 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
243 mLocale = revised;
244
245 synchronized (mAppWidgetIds) {
246 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700247 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
248 // list of installed providers and skip providers that we don't need to update.
249 // Also note that remove the provider does not clear the Provider component data.
250 ArrayList<Provider> installedProviders =
251 new ArrayList<Provider>(mInstalledProviders);
252 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
253 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700254 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700255 Provider p = installedProviders.get(i);
256 ComponentName cn = p.info.provider;
257 if (!removedProviders.contains(cn)) {
258 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
259 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700260 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700261 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700262 }
263 }
264 }
265
266 void onBroadcastReceived(Intent intent) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700267 if (DBG) log("onBroadcast " + intent);
Amith Yamasani742a6712011-05-04 14:49:28 -0700268 final String action = intent.getAction();
269 boolean added = false;
270 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700271 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700272 String pkgList[] = null;
273 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
274 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
275 added = true;
276 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
277 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
278 added = false;
279 } else {
280 Uri uri = intent.getData();
281 if (uri == null) {
282 return;
283 }
284 String pkgName = uri.getSchemeSpecificPart();
285 if (pkgName == null) {
286 return;
287 }
288 pkgList = new String[] { pkgName };
289 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
290 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
291 }
292 if (pkgList == null || pkgList.length == 0) {
293 return;
294 }
295 if (added || changed) {
296 synchronized (mAppWidgetIds) {
297 ensureStateLoadedLocked();
298 Bundle extras = intent.getExtras();
299 if (changed
300 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
301 for (String pkgName : pkgList) {
302 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700303 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700304 }
305 } else {
306 // The package was just added
307 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700308 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700309 }
310 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700311 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700312 }
313 } else {
314 Bundle extras = intent.getExtras();
315 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
316 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
317 } else {
318 synchronized (mAppWidgetIds) {
319 ensureStateLoadedLocked();
320 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700321 providersModified |= removeProvidersForPackageLocked(pkgName);
Adam Cohena1a2f962012-11-01 14:06:16 -0700322 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700323 }
324 }
325 }
326 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700327
328 if (providersModified) {
329 // If the set of providers has been modified, notify each active AppWidgetHost
330 synchronized (mAppWidgetIds) {
331 ensureStateLoadedLocked();
332 notifyHostsForProvidersChangedLocked();
333 }
334 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700335 }
336
337 private void dumpProvider(Provider p, int index, PrintWriter pw) {
338 AppWidgetProviderInfo info = p.info;
339 pw.print(" ["); pw.print(index); pw.print("] provider ");
340 pw.print(info.provider.flattenToShortString());
341 pw.println(':');
342 pw.print(" min=("); pw.print(info.minWidth);
343 pw.print("x"); pw.print(info.minHeight);
344 pw.print(") minResize=("); pw.print(info.minResizeWidth);
345 pw.print("x"); pw.print(info.minResizeHeight);
346 pw.print(") updatePeriodMillis=");
347 pw.print(info.updatePeriodMillis);
348 pw.print(" resizeMode=");
349 pw.print(info.resizeMode);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700350 pw.print(info.widgetCategory);
Amith Yamasani742a6712011-05-04 14:49:28 -0700351 pw.print(" autoAdvanceViewId=");
352 pw.print(info.autoAdvanceViewId);
353 pw.print(" initialLayout=#");
354 pw.print(Integer.toHexString(info.initialLayout));
Amith Yamasani791f8772012-11-21 14:06:07 -0800355 pw.print(" uid="); pw.print(p.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700356 pw.print(" zombie="); pw.println(p.zombie);
357 }
358
359 private void dumpHost(Host host, int index, PrintWriter pw) {
360 pw.print(" ["); pw.print(index); pw.print("] hostId=");
361 pw.print(host.hostId); pw.print(' ');
362 pw.print(host.packageName); pw.print('/');
363 pw.print(host.uid); pw.println(':');
364 pw.print(" callbacks="); pw.println(host.callbacks);
365 pw.print(" instances.size="); pw.print(host.instances.size());
366 pw.print(" zombie="); pw.println(host.zombie);
367 }
368
369 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
370 pw.print(" ["); pw.print(index); pw.print("] id=");
371 pw.println(id.appWidgetId);
372 pw.print(" hostId=");
373 pw.print(id.host.hostId); pw.print(' ');
374 pw.print(id.host.packageName); pw.print('/');
375 pw.println(id.host.uid);
376 if (id.provider != null) {
377 pw.print(" provider=");
378 pw.println(id.provider.info.provider.flattenToShortString());
379 }
380 if (id.host != null) {
381 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
382 }
383 if (id.views != null) {
384 pw.print(" views="); pw.println(id.views);
385 }
386 }
387
388 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
389 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
390 != PackageManager.PERMISSION_GRANTED) {
391 pw.println("Permission Denial: can't dump from from pid="
392 + Binder.getCallingPid()
393 + ", uid=" + Binder.getCallingUid());
394 return;
395 }
396
397 synchronized (mAppWidgetIds) {
398 int N = mInstalledProviders.size();
399 pw.println("Providers:");
400 for (int i=0; i<N; i++) {
401 dumpProvider(mInstalledProviders.get(i), i, pw);
402 }
403
404 N = mAppWidgetIds.size();
405 pw.println(" ");
406 pw.println("AppWidgetIds:");
407 for (int i=0; i<N; i++) {
408 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
409 }
410
411 N = mHosts.size();
412 pw.println(" ");
413 pw.println("Hosts:");
414 for (int i=0; i<N; i++) {
415 dumpHost(mHosts.get(i), i, pw);
416 }
417
418 N = mDeletedProviders.size();
419 pw.println(" ");
420 pw.println("Deleted Providers:");
421 for (int i=0; i<N; i++) {
422 dumpProvider(mDeletedProviders.get(i), i, pw);
423 }
424
425 N = mDeletedHosts.size();
426 pw.println(" ");
427 pw.println("Deleted Hosts:");
428 for (int i=0; i<N; i++) {
429 dumpHost(mDeletedHosts.get(i), i, pw);
430 }
431 }
432 }
433
434 private void ensureStateLoadedLocked() {
435 if (!mStateLoaded) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700436 if (!mHasFeature) {
437 return;
438 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700439 loadAppWidgetListLocked();
Amith Yamasani742a6712011-05-04 14:49:28 -0700440 loadStateLocked();
441 mStateLoaded = true;
442 }
443 }
444
445 public int allocateAppWidgetId(String packageName, int hostId) {
Jim Millerf229e4d2012-09-12 20:32:50 -0700446 int callingUid = enforceSystemOrCallingUid(packageName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700447 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700448 if (!mHasFeature) {
449 return -1;
450 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700451 ensureStateLoadedLocked();
452 int appWidgetId = mNextAppWidgetId++;
453
454 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
455
456 AppWidgetId id = new AppWidgetId();
457 id.appWidgetId = appWidgetId;
458 id.host = host;
459
460 host.instances.add(id);
461 mAppWidgetIds.add(id);
462
Adam Cohena1a2f962012-11-01 14:06:16 -0700463 saveStateAsync();
Amith Yamasani8320de82012-10-05 16:10:38 -0700464 if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
465 + " id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700466 return appWidgetId;
467 }
468 }
469
470 public void deleteAppWidgetId(int appWidgetId) {
471 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700472 if (!mHasFeature) {
473 return;
474 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700475 ensureStateLoadedLocked();
476 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
477 if (id != null) {
478 deleteAppWidgetLocked(id);
Adam Cohena1a2f962012-11-01 14:06:16 -0700479 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700480 }
481 }
482 }
483
484 public void deleteHost(int hostId) {
485 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700486 if (!mHasFeature) {
487 return;
488 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700489 ensureStateLoadedLocked();
490 int callingUid = Binder.getCallingUid();
491 Host host = lookupHostLocked(callingUid, hostId);
492 if (host != null) {
493 deleteHostLocked(host);
Adam Cohena1a2f962012-11-01 14:06:16 -0700494 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700495 }
496 }
497 }
498
499 public void deleteAllHosts() {
500 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700501 if (!mHasFeature) {
502 return;
503 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700504 ensureStateLoadedLocked();
505 int callingUid = Binder.getCallingUid();
506 final int N = mHosts.size();
507 boolean changed = false;
508 for (int i = N - 1; i >= 0; i--) {
509 Host host = mHosts.get(i);
Amith Yamasanic566b432012-11-30 15:26:21 -0800510 if (host.uidMatches(callingUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700511 deleteHostLocked(host);
512 changed = true;
513 }
514 }
515 if (changed) {
Adam Cohena1a2f962012-11-01 14:06:16 -0700516 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700517 }
518 }
519 }
520
521 void deleteHostLocked(Host host) {
522 final int N = host.instances.size();
523 for (int i = N - 1; i >= 0; i--) {
524 AppWidgetId id = host.instances.get(i);
525 deleteAppWidgetLocked(id);
526 }
527 host.instances.clear();
528 mHosts.remove(host);
529 mDeletedHosts.add(host);
530 // it's gone or going away, abruptly drop the callback connection
531 host.callbacks = null;
532 }
533
534 void deleteAppWidgetLocked(AppWidgetId id) {
535 // We first unbind all services that are bound to this id
536 unbindAppWidgetRemoteViewsServicesLocked(id);
537
538 Host host = id.host;
539 host.instances.remove(id);
540 pruneHostLocked(host);
541
542 mAppWidgetIds.remove(id);
543
544 Provider p = id.provider;
545 if (p != null) {
546 p.instances.remove(id);
547 if (!p.zombie) {
548 // send the broacast saying that this appWidgetId has been deleted
549 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
550 intent.setComponent(p.info.provider);
551 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700552 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700553 if (p.instances.size() == 0) {
554 // cancel the future updates
555 cancelBroadcasts(p);
556
557 // send the broacast saying that the provider is not in use any more
558 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
559 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700560 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700561 }
562 }
563 }
564 }
565
566 void cancelBroadcasts(Provider p) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700567 if (DBG) log("cancelBroadcasts for " + p);
Amith Yamasani742a6712011-05-04 14:49:28 -0700568 if (p.broadcast != null) {
569 mAlarmManager.cancel(p.broadcast);
570 long token = Binder.clearCallingIdentity();
571 try {
572 p.broadcast.cancel();
573 } finally {
574 Binder.restoreCallingIdentity(token);
575 }
576 p.broadcast = null;
577 }
578 }
579
Adam Cohen0aa2d422012-09-07 17:37:26 -0700580 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700581 if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
582 + " provider=" + provider);
Amith Yamasani742a6712011-05-04 14:49:28 -0700583 final long ident = Binder.clearCallingIdentity();
584 try {
585 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700586 if (!mHasFeature) {
587 return;
588 }
Adam Cohen3ff2d862012-09-26 14:07:57 -0700589 options = cloneIfLocalBinder(options);
Amith Yamasani742a6712011-05-04 14:49:28 -0700590 ensureStateLoadedLocked();
591 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
592 if (id == null) {
593 throw new IllegalArgumentException("bad appWidgetId");
594 }
595 if (id.provider != null) {
596 throw new IllegalArgumentException("appWidgetId " + appWidgetId
597 + " already bound to " + id.provider.info.provider);
598 }
599 Provider p = lookupProviderLocked(provider);
600 if (p == null) {
601 throw new IllegalArgumentException("not a appwidget provider: " + provider);
602 }
603 if (p.zombie) {
604 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
605 + " safe mode: " + provider);
606 }
607
Amith Yamasani742a6712011-05-04 14:49:28 -0700608 id.provider = p;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700609 if (options == null) {
610 options = new Bundle();
611 }
612 id.options = options;
613
614 // We need to provide a default value for the widget category if it is not specified
615 if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
616 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
617 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
618 }
619
Amith Yamasani742a6712011-05-04 14:49:28 -0700620 p.instances.add(id);
621 int instancesSize = p.instances.size();
622 if (instancesSize == 1) {
623 // tell the provider that it's ready
624 sendEnableIntentLocked(p);
625 }
626
627 // send an update now -- We need this update now, and just for this appWidgetId.
628 // It's less critical when the next one happens, so when we schedule the next one,
629 // we add updatePeriodMillis to its start time. That time will have some slop,
630 // but that's okay.
631 sendUpdateIntentLocked(p, new int[] { appWidgetId });
632
633 // schedule the future updates
634 registerForBroadcastsLocked(p, getAppWidgetIds(p));
Adam Cohena1a2f962012-11-01 14:06:16 -0700635 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700636 }
637 } finally {
638 Binder.restoreCallingIdentity(ident);
639 }
640 }
641
Adam Cohen0aa2d422012-09-07 17:37:26 -0700642 public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka67a871d2012-11-01 18:26:01 -0700643 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET,
Michael Jurka61a5b012012-04-13 10:39:45 -0700644 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700645 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700646 }
647
648 public boolean bindAppWidgetIdIfAllowed(
Adam Cohen0aa2d422012-09-07 17:37:26 -0700649 String packageName, int appWidgetId, ComponentName provider, Bundle options) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700650 if (!mHasFeature) {
651 return false;
652 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700653 try {
Michael Jurka67a871d2012-11-01 18:26:01 -0700654 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
Michael Jurka61a5b012012-04-13 10:39:45 -0700655 } catch (SecurityException se) {
656 if (!callerHasBindAppWidgetPermission(packageName)) {
657 return false;
658 }
659 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700660 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700661 return true;
662 }
663
664 private boolean callerHasBindAppWidgetPermission(String packageName) {
665 int callingUid = Binder.getCallingUid();
666 try {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700667 if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700668 return false;
669 }
670 } catch (Exception e) {
671 return false;
672 }
673 synchronized (mAppWidgetIds) {
674 ensureStateLoadedLocked();
675 return mPackagesWithBindWidgetPermission.contains(packageName);
676 }
677 }
678
679 public boolean hasBindAppWidgetPermission(String packageName) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700680 if (!mHasFeature) {
681 return false;
682 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700683 mContext.enforceCallingPermission(
684 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
685 "hasBindAppWidgetPermission packageName=" + packageName);
686
687 synchronized (mAppWidgetIds) {
688 ensureStateLoadedLocked();
689 return mPackagesWithBindWidgetPermission.contains(packageName);
690 }
691 }
692
693 public void setBindAppWidgetPermission(String packageName, boolean permission) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700694 if (!mHasFeature) {
695 return;
696 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700697 mContext.enforceCallingPermission(
698 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
699 "setBindAppWidgetPermission packageName=" + packageName);
700
701 synchronized (mAppWidgetIds) {
702 ensureStateLoadedLocked();
703 if (permission) {
704 mPackagesWithBindWidgetPermission.add(packageName);
705 } else {
706 mPackagesWithBindWidgetPermission.remove(packageName);
707 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700708 saveStateAsync();
Michael Jurka61a5b012012-04-13 10:39:45 -0700709 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700710 }
711
Amith Yamasani742a6712011-05-04 14:49:28 -0700712 // Binds to a specific RemoteViewsService
713 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
714 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700715 if (!mHasFeature) {
716 return;
717 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700718 ensureStateLoadedLocked();
719 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
720 if (id == null) {
721 throw new IllegalArgumentException("bad appWidgetId");
722 }
723 final ComponentName componentName = intent.getComponent();
724 try {
Amith Yamasani98edc952012-09-25 14:09:27 -0700725 final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
726 PackageManager.GET_PERMISSIONS, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700727 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
728 throw new SecurityException("Selected service does not require "
729 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
730 }
Amith Yamasani98edc952012-09-25 14:09:27 -0700731 } catch (RemoteException e) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700732 throw new IllegalArgumentException("Unknown component " + componentName);
733 }
734
735 // If there is already a connection made for this service intent, then disconnect from
736 // that first. (This does not allow multiple connections to the same service under
737 // the same key)
738 ServiceConnectionProxy conn = null;
739 FilterComparison fc = new FilterComparison(intent);
740 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
741 if (mBoundRemoteViewsServices.containsKey(key)) {
742 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
743 conn.disconnect();
744 mContext.unbindService(conn);
745 mBoundRemoteViewsServices.remove(key);
746 }
747
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700748 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani791f8772012-11-21 14:06:07 -0800749 if (userId != mUserId) {
750 Slog.w(TAG, "AppWidgetServiceImpl of user " + mUserId
751 + " binding to provider on user " + userId);
752 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700753 // Bind to the RemoteViewsService (which will trigger a callback to the
754 // RemoteViewsAdapter.onServiceConnected())
755 final long token = Binder.clearCallingIdentity();
756 try {
757 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani27b89e62013-01-16 12:30:11 -0800758 mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
759 new UserHandle(userId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700760 mBoundRemoteViewsServices.put(key, conn);
761 } finally {
762 Binder.restoreCallingIdentity(token);
763 }
764
765 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
766 // when we can call back to the RemoteViewsService later to destroy associated
767 // factories.
768 incrementAppWidgetServiceRefCount(appWidgetId, fc);
769 }
770 }
771
772 // Unbinds from a specific RemoteViewsService
773 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
774 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700775 if (!mHasFeature) {
776 return;
777 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700778 ensureStateLoadedLocked();
779 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
780 // RemoteViewsAdapter)
781 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
782 intent));
783 if (mBoundRemoteViewsServices.containsKey(key)) {
784 // We don't need to use the appWidgetId until after we are sure there is something
785 // to unbind. Note that this may mask certain issues with apps calling unbind()
786 // more than necessary.
787 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
788 if (id == null) {
789 throw new IllegalArgumentException("bad appWidgetId");
790 }
791
792 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
793 .get(key);
794 conn.disconnect();
795 mContext.unbindService(conn);
796 mBoundRemoteViewsServices.remove(key);
Amith Yamasani742a6712011-05-04 14:49:28 -0700797 }
798 }
799 }
800
801 // Unbinds from a RemoteViewsService when we delete an app widget
802 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
803 int appWidgetId = id.appWidgetId;
804 // Unbind all connections to Services bound to this AppWidgetId
805 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
806 .iterator();
807 while (it.hasNext()) {
808 final Pair<Integer, Intent.FilterComparison> key = it.next();
809 if (key.first.intValue() == appWidgetId) {
810 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
811 .get(key);
812 conn.disconnect();
813 mContext.unbindService(conn);
814 it.remove();
815 }
816 }
817
818 // Check if we need to destroy any services (if no other app widgets are
819 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800820 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700821 }
822
823 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800824 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700825 final ServiceConnection conn = new ServiceConnection() {
826 @Override
827 public void onServiceConnected(ComponentName name, IBinder service) {
828 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
829 try {
830 cb.onDestroy(intent);
831 } catch (RemoteException e) {
832 e.printStackTrace();
833 } catch (RuntimeException e) {
834 e.printStackTrace();
835 }
836 mContext.unbindService(this);
837 }
838
839 @Override
840 public void onServiceDisconnected(android.content.ComponentName name) {
841 // Do nothing
842 }
843 };
844
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700845 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700846 // Bind to the service and remove the static intent->factory mapping in the
847 // RemoteViewsService.
848 final long token = Binder.clearCallingIdentity();
849 try {
Amith Yamasani27b89e62013-01-16 12:30:11 -0800850 mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
851 new UserHandle(userId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700852 } finally {
853 Binder.restoreCallingIdentity(token);
854 }
855 }
856
857 // Adds to the ref-count for a given RemoteViewsService intent
858 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
859 HashSet<Integer> appWidgetIds = null;
860 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
861 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
862 } else {
863 appWidgetIds = new HashSet<Integer>();
864 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
865 }
866 appWidgetIds.add(appWidgetId);
867 }
868
869 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
870 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800871 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700872 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
873 while (it.hasNext()) {
874 final FilterComparison key = it.next();
875 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800876 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700877 // If we have removed the last app widget referencing this service, then we
878 // should destroy it and remove it from this set
879 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800880 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700881 it.remove();
882 }
883 }
884 }
885 }
886
887 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
888 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700889 if (!mHasFeature) {
890 return null;
891 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700892 ensureStateLoadedLocked();
893 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
894 if (id != null && id.provider != null && !id.provider.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700895 return cloneIfLocalBinder(id.provider.info);
Amith Yamasani742a6712011-05-04 14:49:28 -0700896 }
897 return null;
898 }
899 }
900
901 public RemoteViews getAppWidgetViews(int appWidgetId) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700902 if (DBG) log("getAppWidgetViews id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700903 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700904 if (!mHasFeature) {
905 return null;
906 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700907 ensureStateLoadedLocked();
908 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
909 if (id != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700910 return cloneIfLocalBinder(id.views);
Amith Yamasani742a6712011-05-04 14:49:28 -0700911 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700912 if (DBG) log(" couldn't find appwidgetid");
Amith Yamasani742a6712011-05-04 14:49:28 -0700913 return null;
914 }
915 }
916
Adam Cohend9e5af32012-11-28 16:34:57 -0800917 public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700918 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700919 if (!mHasFeature) {
920 return new ArrayList<AppWidgetProviderInfo>(0);
921 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700922 ensureStateLoadedLocked();
923 final int N = mInstalledProviders.size();
924 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
925 for (int i = 0; i < N; i++) {
926 Provider p = mInstalledProviders.get(i);
Adam Cohen3fcc6b22012-11-15 14:10:07 -0800927 if (!p.zombie && (p.info.widgetCategory & categoryFilter) != 0) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700928 result.add(cloneIfLocalBinder(p.info));
Amith Yamasani742a6712011-05-04 14:49:28 -0700929 }
930 }
931 return result;
932 }
933 }
934
935 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700936 if (!mHasFeature) {
937 return;
938 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700939 if (appWidgetIds == null) {
940 return;
941 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700942 if (DBG) log("updateAppWidgetIds views: " + views);
Adam Cohenf08a8b72012-07-16 12:02:10 -0700943 int bitmapMemoryUsage = 0;
944 if (views != null) {
945 bitmapMemoryUsage = views.estimateMemoryUsage();
946 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700947 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
948 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
949 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
950 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
951 " fill the device's screen once.");
952 }
953
Amith Yamasani742a6712011-05-04 14:49:28 -0700954 if (appWidgetIds.length == 0) {
955 return;
956 }
957 final int N = appWidgetIds.length;
958
959 synchronized (mAppWidgetIds) {
960 ensureStateLoadedLocked();
961 for (int i = 0; i < N; i++) {
962 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
963 updateAppWidgetInstanceLocked(id, views);
964 }
965 }
966 }
967
Adam Cohena1a2f962012-11-01 14:06:16 -0700968 private void saveStateAsync() {
969 mSaveStateHandler.post(mSaveStateRunnable);
970 }
971
972 private final Runnable mSaveStateRunnable = new Runnable() {
973 @Override
974 public void run() {
975 synchronized (mAppWidgetIds) {
976 ensureStateLoadedLocked();
977 saveStateLocked();
978 }
979 }
980 };
981
Adam Cohend2097eb2012-05-01 18:10:28 -0700982 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700983 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700984 if (!mHasFeature) {
985 return;
986 }
Adam Cohen3ff2d862012-09-26 14:07:57 -0700987 options = cloneIfLocalBinder(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700988 ensureStateLoadedLocked();
989 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
990
991 if (id == null) {
992 return;
993 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700994
Adam Cohene8724c82012-04-19 17:11:40 -0700995 Provider p = id.provider;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700996 // Merge the options
997 id.options.putAll(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700998
999 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -07001000 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -07001001 intent.setComponent(p.info.provider);
1002 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001003 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001004 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Adam Cohena1a2f962012-11-01 14:06:16 -07001005 saveStateAsync();
Adam Cohene8724c82012-04-19 17:11:40 -07001006 }
1007 }
1008
Adam Cohend2097eb2012-05-01 18:10:28 -07001009 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -07001010 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001011 if (!mHasFeature) {
1012 return Bundle.EMPTY;
1013 }
Adam Cohene8724c82012-04-19 17:11:40 -07001014 ensureStateLoadedLocked();
1015 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -07001016 if (id != null && id.options != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -07001017 return cloneIfLocalBinder(id.options);
Adam Cohene8724c82012-04-19 17:11:40 -07001018 } else {
1019 return Bundle.EMPTY;
1020 }
1021 }
1022 }
1023
Amith Yamasani742a6712011-05-04 14:49:28 -07001024 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001025 if (!mHasFeature) {
1026 return;
1027 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001028 if (appWidgetIds == null) {
1029 return;
1030 }
1031 if (appWidgetIds.length == 0) {
1032 return;
1033 }
1034 final int N = appWidgetIds.length;
1035
1036 synchronized (mAppWidgetIds) {
1037 ensureStateLoadedLocked();
1038 for (int i = 0; i < N; i++) {
1039 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Amith Yamasani791f8772012-11-21 14:06:07 -08001040 if (id == null) {
Amith Yamasanic566b432012-11-30 15:26:21 -08001041 Slog.w(TAG, "widget id " + appWidgetIds[i] + " not found!");
1042 } else if (id.views != null) {
Winson Chung66119882012-10-11 14:26:25 -07001043 // Only trigger a partial update for a widget if it has received a full update
1044 updateAppWidgetInstanceLocked(id, views, true);
1045 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001046 }
1047 }
1048 }
1049
1050 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001051 if (!mHasFeature) {
1052 return;
1053 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001054 if (appWidgetIds == null) {
1055 return;
1056 }
1057 if (appWidgetIds.length == 0) {
1058 return;
1059 }
1060 final int N = appWidgetIds.length;
1061
1062 synchronized (mAppWidgetIds) {
1063 ensureStateLoadedLocked();
1064 for (int i = 0; i < N; i++) {
1065 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
1066 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
1067 }
1068 }
1069 }
1070
1071 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001072 if (!mHasFeature) {
1073 return;
1074 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001075 synchronized (mAppWidgetIds) {
1076 ensureStateLoadedLocked();
1077 Provider p = lookupProviderLocked(provider);
1078 if (p == null) {
1079 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
1080 return;
1081 }
1082 ArrayList<AppWidgetId> instances = p.instances;
1083 final int callingUid = Binder.getCallingUid();
1084 final int N = instances.size();
1085 for (int i = 0; i < N; i++) {
1086 AppWidgetId id = instances.get(i);
1087 if (canAccessAppWidgetId(id, callingUid)) {
1088 updateAppWidgetInstanceLocked(id, views);
1089 }
1090 }
1091 }
1092 }
1093
1094 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
1095 updateAppWidgetInstanceLocked(id, views, false);
1096 }
1097
1098 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
1099 // allow for stale appWidgetIds and other badness
1100 // lookup also checks that the calling process can access the appWidgetId
1101 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1102 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1103
Winson Chung66119882012-10-11 14:26:25 -07001104 if (!isPartialUpdate) {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001105 // For a full update we replace the RemoteViews completely.
Amith Yamasani742a6712011-05-04 14:49:28 -07001106 id.views = views;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001107 } else {
1108 // For a partial update, we merge the new RemoteViews with the old.
1109 id.views.mergeRemoteViews(views);
1110 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001111
1112 // is anyone listening?
1113 if (id.host.callbacks != null) {
1114 try {
1115 // the lock is held, but this is a oneway call
Jim Millera75a8832013-02-07 16:53:32 -08001116 id.host.callbacks.updateAppWidget(id.appWidgetId, views, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001117 } catch (RemoteException e) {
1118 // It failed; remove the callback. No need to prune because
1119 // we know that this host is still referenced by this instance.
1120 id.host.callbacks = null;
1121 }
1122 }
1123 }
1124 }
1125
1126 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1127 // allow for stale appWidgetIds and other badness
1128 // lookup also checks that the calling process can access the appWidgetId
1129 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1130 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1131 // is anyone listening?
1132 if (id.host.callbacks != null) {
1133 try {
1134 // the lock is held, but this is a oneway call
Jim Millera75a8832013-02-07 16:53:32 -08001135 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001136 } catch (RemoteException e) {
1137 // It failed; remove the callback. No need to prune because
1138 // we know that this host is still referenced by this instance.
1139 id.host.callbacks = null;
1140 }
1141 }
1142
1143 // If the host is unavailable, then we call the associated
1144 // RemoteViewsFactory.onDataSetChanged() directly
1145 if (id.host.callbacks == null) {
1146 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1147 for (FilterComparison key : keys) {
1148 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1149 Intent intent = key.getIntent();
1150
1151 final ServiceConnection conn = new ServiceConnection() {
1152 @Override
1153 public void onServiceConnected(ComponentName name, IBinder service) {
1154 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1155 .asInterface(service);
1156 try {
1157 cb.onDataSetChangedAsync();
1158 } catch (RemoteException e) {
1159 e.printStackTrace();
1160 } catch (RuntimeException e) {
1161 e.printStackTrace();
1162 }
1163 mContext.unbindService(this);
1164 }
1165
1166 @Override
1167 public void onServiceDisconnected(android.content.ComponentName name) {
1168 // Do nothing
1169 }
1170 };
1171
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001172 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001173 // Bind to the service and call onDataSetChanged()
1174 final long token = Binder.clearCallingIdentity();
1175 try {
Amith Yamasani27b89e62013-01-16 12:30:11 -08001176 mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
1177 new UserHandle(userId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001178 } finally {
1179 Binder.restoreCallingIdentity(token);
1180 }
1181 }
1182 }
1183 }
1184 }
1185 }
1186
Adam Cohen3ff2d862012-09-26 14:07:57 -07001187 private boolean isLocalBinder() {
1188 return Process.myPid() == Binder.getCallingPid();
1189 }
1190
1191 private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1192 if (isLocalBinder() && rv != null) {
1193 return rv.clone();
1194 }
1195 return rv;
1196 }
1197
1198 private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
1199 if (isLocalBinder() && info != null) {
1200 return info.clone();
1201 }
1202 return info;
1203 }
1204
1205 private Bundle cloneIfLocalBinder(Bundle bundle) {
1206 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
1207 // if we start adding objects to the options. Further, it would only be an issue if keyguard
1208 // used such options.
1209 if (isLocalBinder() && bundle != null) {
1210 return (Bundle) bundle.clone();
1211 }
1212 return bundle;
1213 }
1214
Amith Yamasani742a6712011-05-04 14:49:28 -07001215 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1216 List<RemoteViews> updatedViews) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001217 if (!mHasFeature) {
1218 return new int[0];
1219 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001220 int callingUid = enforceCallingUid(packageName);
1221 synchronized (mAppWidgetIds) {
1222 ensureStateLoadedLocked();
1223 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1224 host.callbacks = callbacks;
1225
1226 updatedViews.clear();
1227
1228 ArrayList<AppWidgetId> instances = host.instances;
1229 int N = instances.size();
1230 int[] updatedIds = new int[N];
1231 for (int i = 0; i < N; i++) {
1232 AppWidgetId id = instances.get(i);
1233 updatedIds[i] = id.appWidgetId;
Adam Cohen3ff2d862012-09-26 14:07:57 -07001234 updatedViews.add(cloneIfLocalBinder(id.views));
Amith Yamasani742a6712011-05-04 14:49:28 -07001235 }
1236 return updatedIds;
1237 }
1238 }
1239
1240 public void stopListening(int hostId) {
1241 synchronized (mAppWidgetIds) {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001242 if (!mHasFeature) {
1243 return;
1244 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001245 ensureStateLoadedLocked();
1246 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1247 if (host != null) {
1248 host.callbacks = null;
1249 pruneHostLocked(host);
1250 }
1251 }
1252 }
1253
1254 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
Amith Yamasanic566b432012-11-30 15:26:21 -08001255 if (id.host.uidMatches(callingUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001256 // Apps hosting the AppWidget have access to it.
1257 return true;
1258 }
1259 if (id.provider != null && id.provider.uid == callingUid) {
1260 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1261 return true;
1262 }
1263 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1264 // Apps that can bind have access to all appWidgetIds.
1265 return true;
1266 }
1267 // Nobody else can access it.
1268 return false;
1269 }
1270
1271 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1272 int callingUid = Binder.getCallingUid();
1273 final int N = mAppWidgetIds.size();
1274 for (int i = 0; i < N; i++) {
1275 AppWidgetId id = mAppWidgetIds.get(i);
1276 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1277 return id;
1278 }
1279 }
1280 return null;
1281 }
1282
1283 Provider lookupProviderLocked(ComponentName provider) {
1284 final int N = mInstalledProviders.size();
1285 for (int i = 0; i < N; i++) {
1286 Provider p = mInstalledProviders.get(i);
1287 if (p.info.provider.equals(provider)) {
1288 return p;
1289 }
1290 }
1291 return null;
1292 }
1293
1294 Host lookupHostLocked(int uid, int hostId) {
1295 final int N = mHosts.size();
1296 for (int i = 0; i < N; i++) {
1297 Host h = mHosts.get(i);
Amith Yamasanic566b432012-11-30 15:26:21 -08001298 if (h.uidMatches(uid) && h.hostId == hostId) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001299 return h;
1300 }
1301 }
1302 return null;
1303 }
1304
1305 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1306 final int N = mHosts.size();
1307 for (int i = 0; i < N; i++) {
1308 Host h = mHosts.get(i);
1309 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1310 return h;
1311 }
1312 }
1313 Host host = new Host();
1314 host.packageName = packageName;
1315 host.uid = uid;
1316 host.hostId = hostId;
1317 mHosts.add(host);
1318 return host;
1319 }
1320
1321 void pruneHostLocked(Host host) {
1322 if (host.instances.size() == 0 && host.callbacks == null) {
1323 mHosts.remove(host);
1324 }
1325 }
1326
Adam Cohena1a2f962012-11-01 14:06:16 -07001327 void loadAppWidgetListLocked() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001328 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001329 try {
1330 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1331 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1332 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001333
Amith Yamasani483f3b02012-03-13 16:08:00 -07001334 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1335 for (int i = 0; i < N; i++) {
1336 ResolveInfo ri = broadcastReceivers.get(i);
1337 addProviderLocked(ri);
1338 }
1339 } catch (RemoteException re) {
1340 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001341 }
1342 }
1343
1344 boolean addProviderLocked(ResolveInfo ri) {
1345 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1346 return false;
1347 }
1348 if (!ri.activityInfo.isEnabled()) {
1349 return false;
1350 }
1351 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1352 ri.activityInfo.name), ri);
1353 if (p != null) {
1354 mInstalledProviders.add(p);
1355 return true;
1356 } else {
1357 return false;
1358 }
1359 }
1360
1361 void removeProviderLocked(int index, Provider p) {
1362 int N = p.instances.size();
1363 for (int i = 0; i < N; i++) {
1364 AppWidgetId id = p.instances.get(i);
1365 // Call back with empty RemoteViews
1366 updateAppWidgetInstanceLocked(id, null);
1367 // Stop telling the host about updates for this from now on
1368 cancelBroadcasts(p);
1369 // clear out references to this appWidgetId
1370 id.host.instances.remove(id);
1371 mAppWidgetIds.remove(id);
1372 id.provider = null;
1373 pruneHostLocked(id.host);
1374 id.host = null;
1375 }
1376 p.instances.clear();
1377 mInstalledProviders.remove(index);
1378 mDeletedProviders.add(p);
1379 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1380 cancelBroadcasts(p);
1381 }
1382
1383 void sendEnableIntentLocked(Provider p) {
1384 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1385 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001386 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001387 }
1388
1389 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1390 if (appWidgetIds != null && appWidgetIds.length > 0) {
1391 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1392 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1393 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001394 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001395 }
1396 }
1397
1398 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1399 if (p.info.updatePeriodMillis > 0) {
1400 // if this is the first instance, set the alarm. otherwise,
1401 // rely on the fact that we've already set it and that
1402 // PendingIntent.getBroadcast will update the extras.
1403 boolean alreadyRegistered = p.broadcast != null;
1404 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1405 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1406 intent.setComponent(p.info.provider);
1407 long token = Binder.clearCallingIdentity();
1408 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001409 p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
1410 PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001411 } finally {
1412 Binder.restoreCallingIdentity(token);
1413 }
1414 if (!alreadyRegistered) {
1415 long period = p.info.updatePeriodMillis;
1416 if (period < MIN_UPDATE_PERIOD) {
1417 period = MIN_UPDATE_PERIOD;
1418 }
1419 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1420 .elapsedRealtime()
1421 + period, period, p.broadcast);
1422 }
1423 }
1424 }
1425
1426 static int[] getAppWidgetIds(Provider p) {
1427 int instancesSize = p.instances.size();
1428 int appWidgetIds[] = new int[instancesSize];
1429 for (int i = 0; i < instancesSize; i++) {
1430 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1431 }
1432 return appWidgetIds;
1433 }
1434
1435 public int[] getAppWidgetIds(ComponentName provider) {
1436 synchronized (mAppWidgetIds) {
1437 ensureStateLoadedLocked();
1438 Provider p = lookupProviderLocked(provider);
1439 if (p != null && Binder.getCallingUid() == p.uid) {
1440 return getAppWidgetIds(p);
1441 } else {
1442 return new int[0];
1443 }
1444 }
1445 }
1446
Michael Jurka75b5cfb2012-11-15 18:22:47 -08001447 static int[] getAppWidgetIds(Host h) {
1448 int instancesSize = h.instances.size();
1449 int appWidgetIds[] = new int[instancesSize];
1450 for (int i = 0; i < instancesSize; i++) {
1451 appWidgetIds[i] = h.instances.get(i).appWidgetId;
1452 }
1453 return appWidgetIds;
1454 }
1455
1456 public int[] getAppWidgetIdsForHost(int hostId) {
1457 synchronized (mAppWidgetIds) {
1458 ensureStateLoadedLocked();
1459 int callingUid = Binder.getCallingUid();
1460 Host host = lookupHostLocked(callingUid, hostId);
1461 if (host != null) {
1462 return getAppWidgetIds(host);
1463 } else {
1464 return new int[0];
1465 }
1466 }
1467 }
1468
Amith Yamasani742a6712011-05-04 14:49:28 -07001469 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1470 Provider p = null;
1471
1472 ActivityInfo activityInfo = ri.activityInfo;
1473 XmlResourceParser parser = null;
1474 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001475 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001476 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1477 if (parser == null) {
1478 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1479 + " meta-data for " + "AppWidget provider '" + component + '\'');
1480 return null;
1481 }
1482
1483 AttributeSet attrs = Xml.asAttributeSet(parser);
1484
1485 int type;
1486 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1487 && type != XmlPullParser.START_TAG) {
1488 // drain whitespace, comments, etc.
1489 }
1490
1491 String nodeName = parser.getName();
1492 if (!"appwidget-provider".equals(nodeName)) {
1493 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1494 + " AppWidget provider '" + component + '\'');
1495 return null;
1496 }
1497
1498 p = new Provider();
1499 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1500 info.provider = component;
1501 p.uid = activityInfo.applicationInfo.uid;
1502
Amith Yamasani483f3b02012-03-13 16:08:00 -07001503 Resources res = mContext.getPackageManager()
Amith Yamasani8320de82012-10-05 16:10:38 -07001504 .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001505
1506 TypedArray sa = res.obtainAttributes(attrs,
1507 com.android.internal.R.styleable.AppWidgetProviderInfo);
1508
1509 // These dimensions has to be resolved in the application's context.
1510 // We simply send back the raw complex data, which will be
1511 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1512 TypedValue value = sa
1513 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1514 info.minWidth = value != null ? value.data : 0;
1515 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1516 info.minHeight = value != null ? value.data : 0;
1517 value = sa.peekValue(
1518 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1519 info.minResizeWidth = value != null ? value.data : info.minWidth;
1520 value = sa.peekValue(
1521 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1522 info.minResizeHeight = value != null ? value.data : info.minHeight;
1523 info.updatePeriodMillis = sa.getInt(
1524 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1525 info.initialLayout = sa.getResourceId(
1526 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001527 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1528 AppWidgetProviderInfo_initialKeyguardLayout, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001529 String className = sa
1530 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1531 if (className != null) {
1532 info.configure = new ComponentName(component.getPackageName(), className);
1533 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001534 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001535 info.icon = ri.getIconResource();
1536 info.previewImage = sa.getResourceId(
1537 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1538 info.autoAdvanceViewId = sa.getResourceId(
1539 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1540 info.resizeMode = sa.getInt(
1541 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1542 AppWidgetProviderInfo.RESIZE_NONE);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001543 info.widgetCategory = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001544 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001545 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
Amith Yamasani742a6712011-05-04 14:49:28 -07001546
1547 sa.recycle();
1548 } catch (Exception e) {
1549 // Ok to catch Exception here, because anything going wrong because
1550 // of what a client process passes to us should not be fatal for the
1551 // system process.
1552 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1553 return null;
1554 } finally {
1555 if (parser != null)
1556 parser.close();
1557 }
1558 return p;
1559 }
1560
1561 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001562 PackageInfo pkgInfo = null;
1563 try {
1564 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1565 } catch (RemoteException re) {
1566 // Shouldn't happen, local call
1567 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001568 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1569 throw new PackageManager.NameNotFoundException();
1570 }
1571 return pkgInfo.applicationInfo.uid;
1572 }
1573
Jim Millerf229e4d2012-09-12 20:32:50 -07001574 int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1575 int callingUid = Binder.getCallingUid();
Michael Jurka03bdc8a2012-09-21 16:10:21 -07001576 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
Jim Millerf229e4d2012-09-12 20:32:50 -07001577 return callingUid;
1578 }
1579 return enforceCallingUid(packageName);
1580 }
1581
Amith Yamasani742a6712011-05-04 14:49:28 -07001582 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1583 int callingUid = Binder.getCallingUid();
1584 int packageUid;
1585 try {
1586 packageUid = getUidForPackage(packageName);
1587 } catch (PackageManager.NameNotFoundException ex) {
1588 throw new IllegalArgumentException("packageName and uid don't match packageName="
1589 + packageName);
1590 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001591 if (!UserHandle.isSameApp(callingUid, packageUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001592 throw new IllegalArgumentException("packageName and uid don't match packageName="
1593 + packageName);
1594 }
1595 return callingUid;
1596 }
1597
1598 void sendInitialBroadcasts() {
1599 synchronized (mAppWidgetIds) {
1600 ensureStateLoadedLocked();
1601 final int N = mInstalledProviders.size();
1602 for (int i = 0; i < N; i++) {
1603 Provider p = mInstalledProviders.get(i);
1604 if (p.instances.size() > 0) {
1605 sendEnableIntentLocked(p);
1606 int[] appWidgetIds = getAppWidgetIds(p);
1607 sendUpdateIntentLocked(p, appWidgetIds);
1608 registerForBroadcastsLocked(p, appWidgetIds);
1609 }
1610 }
1611 }
1612 }
1613
1614 // only call from initialization -- it assumes that the data structures are all empty
1615 void loadStateLocked() {
1616 AtomicFile file = savedStateFile();
1617 try {
1618 FileInputStream stream = file.openRead();
1619 readStateFromFileLocked(stream);
1620
1621 if (stream != null) {
1622 try {
1623 stream.close();
1624 } catch (IOException e) {
1625 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1626 }
1627 }
1628 } catch (FileNotFoundException e) {
1629 Slog.w(TAG, "Failed to read state: " + e);
1630 }
1631 }
1632
1633 void saveStateLocked() {
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001634 if (!mHasFeature) {
1635 return;
1636 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001637 AtomicFile file = savedStateFile();
1638 FileOutputStream stream;
1639 try {
1640 stream = file.startWrite();
1641 if (writeStateToFileLocked(stream)) {
1642 file.finishWrite(stream);
1643 } else {
1644 file.failWrite(stream);
1645 Slog.w(TAG, "Failed to save state, restoring backup.");
1646 }
1647 } catch (IOException e) {
1648 Slog.w(TAG, "Failed open state file for write: " + e);
1649 }
1650 }
1651
1652 boolean writeStateToFileLocked(FileOutputStream stream) {
1653 int N;
1654
1655 try {
1656 XmlSerializer out = new FastXmlSerializer();
1657 out.setOutput(stream, "utf-8");
1658 out.startDocument(null, true);
1659 out.startTag(null, "gs");
Jim Miller39d129e2013-03-07 16:28:24 -08001660 out.attribute(null, "version", String.valueOf(CURRENT_VERSION));
Amith Yamasani742a6712011-05-04 14:49:28 -07001661 int providerIndex = 0;
1662 N = mInstalledProviders.size();
1663 for (int i = 0; i < N; i++) {
1664 Provider p = mInstalledProviders.get(i);
1665 if (p.instances.size() > 0) {
1666 out.startTag(null, "p");
1667 out.attribute(null, "pkg", p.info.provider.getPackageName());
1668 out.attribute(null, "cl", p.info.provider.getClassName());
1669 out.endTag(null, "p");
1670 p.tag = providerIndex;
1671 providerIndex++;
1672 }
1673 }
1674
1675 N = mHosts.size();
1676 for (int i = 0; i < N; i++) {
1677 Host host = mHosts.get(i);
1678 out.startTag(null, "h");
1679 out.attribute(null, "pkg", host.packageName);
1680 out.attribute(null, "id", Integer.toHexString(host.hostId));
1681 out.endTag(null, "h");
1682 host.tag = i;
1683 }
1684
1685 N = mAppWidgetIds.size();
1686 for (int i = 0; i < N; i++) {
1687 AppWidgetId id = mAppWidgetIds.get(i);
1688 out.startTag(null, "g");
1689 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1690 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1691 if (id.provider != null) {
1692 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1693 }
Adam Cohen0aa2d422012-09-07 17:37:26 -07001694 if (id.options != null) {
1695 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1696 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1697 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1698 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1699 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1700 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1701 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1702 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1703 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1704 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1705 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001706 out.endTag(null, "g");
1707 }
1708
Michael Jurka61a5b012012-04-13 10:39:45 -07001709 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1710 while (it.hasNext()) {
1711 out.startTag(null, "b");
1712 out.attribute(null, "packageName", it.next());
1713 out.endTag(null, "b");
1714 }
1715
Amith Yamasani742a6712011-05-04 14:49:28 -07001716 out.endTag(null, "gs");
1717
1718 out.endDocument();
1719 return true;
1720 } catch (IOException e) {
1721 Slog.w(TAG, "Failed to write state: " + e);
1722 return false;
1723 }
1724 }
1725
Adam Cohen0aa2d422012-09-07 17:37:26 -07001726 @SuppressWarnings("unused")
Amith Yamasani742a6712011-05-04 14:49:28 -07001727 void readStateFromFileLocked(FileInputStream stream) {
1728 boolean success = false;
Jim Miller39d129e2013-03-07 16:28:24 -08001729 int version = 0;
Amith Yamasani742a6712011-05-04 14:49:28 -07001730 try {
1731 XmlPullParser parser = Xml.newPullParser();
1732 parser.setInput(stream, null);
1733
1734 int type;
1735 int providerIndex = 0;
1736 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1737 do {
1738 type = parser.next();
1739 if (type == XmlPullParser.START_TAG) {
1740 String tag = parser.getName();
Jim Miller39d129e2013-03-07 16:28:24 -08001741 if ("gs".equals(tag)) {
1742 String attributeValue = parser.getAttributeValue(null, "version");
1743 try {
1744 version = Integer.parseInt(attributeValue);
1745 } catch (NumberFormatException e) {
1746 version = 0;
1747 }
1748 } else if ("p".equals(tag)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001749 // TODO: do we need to check that this package has the same signature
1750 // as before?
1751 String pkg = parser.getAttributeValue(null, "pkg");
1752 String cl = parser.getAttributeValue(null, "cl");
1753
Amith Yamasanif203aee2012-08-29 18:41:53 -07001754 final IPackageManager packageManager = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -07001755 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001756 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
Amith Yamasanif203aee2012-08-29 18:41:53 -07001757 } catch (RemoteException e) {
1758 String[] pkgs = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001759 .currentToCanonicalPackageNames(new String[] { pkg });
1760 pkg = pkgs[0];
1761 }
1762
1763 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1764 if (p == null && mSafeMode) {
1765 // if we're in safe mode, make a temporary one
1766 p = new Provider();
1767 p.info = new AppWidgetProviderInfo();
1768 p.info.provider = new ComponentName(pkg, cl);
1769 p.zombie = true;
1770 mInstalledProviders.add(p);
1771 }
1772 if (p != null) {
1773 // if it wasn't uninstalled or something
1774 loadedProviders.put(providerIndex, p);
1775 }
1776 providerIndex++;
1777 } else if ("h".equals(tag)) {
1778 Host host = new Host();
1779
1780 // TODO: do we need to check that this package has the same signature
1781 // as before?
1782 host.packageName = parser.getAttributeValue(null, "pkg");
1783 try {
1784 host.uid = getUidForPackage(host.packageName);
1785 } catch (PackageManager.NameNotFoundException ex) {
1786 host.zombie = true;
1787 }
1788 if (!host.zombie || mSafeMode) {
1789 // In safe mode, we don't discard the hosts we don't recognize
1790 // so that they're not pruned from our list. Otherwise, we do.
1791 host.hostId = Integer
1792 .parseInt(parser.getAttributeValue(null, "id"), 16);
1793 mHosts.add(host);
1794 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001795 } else if ("b".equals(tag)) {
1796 String packageName = parser.getAttributeValue(null, "packageName");
1797 if (packageName != null) {
1798 mPackagesWithBindWidgetPermission.add(packageName);
1799 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001800 } else if ("g".equals(tag)) {
1801 AppWidgetId id = new AppWidgetId();
1802 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1803 if (id.appWidgetId >= mNextAppWidgetId) {
1804 mNextAppWidgetId = id.appWidgetId + 1;
1805 }
1806
Adam Cohen0aa2d422012-09-07 17:37:26 -07001807 Bundle options = new Bundle();
1808 String minWidthString = parser.getAttributeValue(null, "min_width");
1809 if (minWidthString != null) {
1810 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1811 Integer.parseInt(minWidthString, 16));
1812 }
1813 String minHeightString = parser.getAttributeValue(null, "min_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001814 if (minHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001815 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1816 Integer.parseInt(minHeightString, 16));
1817 }
Adam Cohendb38d8a2012-09-21 18:14:58 -07001818 String maxWidthString = parser.getAttributeValue(null, "max_width");
1819 if (maxWidthString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001820 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1821 Integer.parseInt(maxWidthString, 16));
1822 }
1823 String maxHeightString = parser.getAttributeValue(null, "max_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001824 if (maxHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001825 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1826 Integer.parseInt(maxHeightString, 16));
1827 }
1828 String categoryString = parser.getAttributeValue(null, "host_category");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001829 if (categoryString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001830 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1831 Integer.parseInt(categoryString, 16));
1832 }
1833 id.options = options;
1834
Amith Yamasani742a6712011-05-04 14:49:28 -07001835 String providerString = parser.getAttributeValue(null, "p");
1836 if (providerString != null) {
1837 // there's no provider if it hasn't been bound yet.
1838 // maybe we don't have to save this, but it brings the system
1839 // to the state it was in.
1840 int pIndex = Integer.parseInt(providerString, 16);
1841 id.provider = loadedProviders.get(pIndex);
1842 if (false) {
1843 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1844 + pIndex + " which is " + id.provider);
1845 }
1846 if (id.provider == null) {
1847 // This provider is gone. We just let the host figure out
1848 // that this happened when it fails to load it.
1849 continue;
1850 }
1851 }
1852
1853 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1854 id.host = mHosts.get(hIndex);
1855 if (id.host == null) {
1856 // This host is gone.
1857 continue;
1858 }
1859
1860 if (id.provider != null) {
1861 id.provider.instances.add(id);
1862 }
1863 id.host.instances.add(id);
1864 mAppWidgetIds.add(id);
1865 }
1866 }
1867 } while (type != XmlPullParser.END_DOCUMENT);
1868 success = true;
1869 } catch (NullPointerException e) {
1870 Slog.w(TAG, "failed parsing " + e);
1871 } catch (NumberFormatException e) {
1872 Slog.w(TAG, "failed parsing " + e);
1873 } catch (XmlPullParserException e) {
1874 Slog.w(TAG, "failed parsing " + e);
1875 } catch (IOException e) {
1876 Slog.w(TAG, "failed parsing " + e);
1877 } catch (IndexOutOfBoundsException e) {
1878 Slog.w(TAG, "failed parsing " + e);
1879 }
1880
1881 if (success) {
1882 // delete any hosts that didn't manage to get connected (should happen)
1883 // if it matters, they'll be reconnected.
1884 for (int i = mHosts.size() - 1; i >= 0; i--) {
1885 pruneHostLocked(mHosts.get(i));
1886 }
Jim Miller39d129e2013-03-07 16:28:24 -08001887 // upgrade the database if needed
1888 performUpgrade(version);
Amith Yamasani742a6712011-05-04 14:49:28 -07001889 } else {
1890 // failed reading, clean up
1891 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1892
1893 mAppWidgetIds.clear();
1894 mHosts.clear();
1895 final int N = mInstalledProviders.size();
1896 for (int i = 0; i < N; i++) {
1897 mInstalledProviders.get(i).instances.clear();
1898 }
1899 }
1900 }
1901
Jim Miller39d129e2013-03-07 16:28:24 -08001902 private void performUpgrade(int fromVersion) {
1903 if (fromVersion < CURRENT_VERSION) {
1904 Slog.v(TAG, "Upgrading widget database from " + fromVersion + " to " + CURRENT_VERSION
1905 + " for user " + mUserId);
1906 }
1907
1908 int version = fromVersion;
1909
1910 // Update 1: keyguard moved from package "android" to "com.android.keyguard"
1911 if (version == 0) {
1912 for (int i = 0; i < mHosts.size(); i++) {
1913 Host host = mHosts.get(i);
1914 if (host != null && "android".equals(host.packageName)
1915 && host.hostId == KEYGUARD_HOST_ID) {
1916 host.packageName = KEYGUARD_HOST_PACKAGE;
1917 }
1918 }
1919 version = 1;
1920 }
1921
1922 if (version != CURRENT_VERSION) {
1923 throw new IllegalStateException("Failed to upgrade widget database");
1924 }
1925 }
1926
Amith Yamasani13593602012-03-22 16:16:17 -07001927 static File getSettingsFile(int userId) {
Amith Yamasani61f57372012-08-31 12:12:28 -07001928 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
Amith Yamasani13593602012-03-22 16:16:17 -07001929 }
1930
Amith Yamasani742a6712011-05-04 14:49:28 -07001931 AtomicFile savedStateFile() {
Amith Yamasani61f57372012-08-31 12:12:28 -07001932 File dir = Environment.getUserSystemDirectory(mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001933 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001934 if (!settingsFile.exists() && mUserId == 0) {
1935 if (!dir.exists()) {
1936 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001937 }
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001938 // Migrate old data
1939 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1940 // Method doesn't throw an exception on failure. Ignore any errors
1941 // in moving the file (like non-existence)
1942 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001943 }
1944 return new AtomicFile(settingsFile);
1945 }
1946
Amith Yamasani756901d2012-10-12 12:30:07 -07001947 void onUserStopping() {
Amith Yamasani13593602012-03-22 16:16:17 -07001948 // prune the ones we don't want to keep
1949 int N = mInstalledProviders.size();
1950 for (int i = N - 1; i >= 0; i--) {
1951 Provider p = mInstalledProviders.get(i);
1952 cancelBroadcasts(p);
1953 }
Amith Yamasani756901d2012-10-12 12:30:07 -07001954 }
1955
1956 void onUserRemoved() {
Amith Yamasani13593602012-03-22 16:16:17 -07001957 getSettingsFile(mUserId).delete();
1958 }
1959
Winson Chung7fbd2842012-06-13 10:35:51 -07001960 boolean addProvidersForPackageLocked(String pkgName) {
1961 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001962 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1963 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001964 List<ResolveInfo> broadcastReceivers;
1965 try {
1966 broadcastReceivers = mPm.queryIntentReceivers(intent,
1967 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1968 PackageManager.GET_META_DATA, mUserId);
1969 } catch (RemoteException re) {
1970 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001971 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001972 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001973 final 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 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001982 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001983 }
1984 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001985
1986 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001987 }
1988
Winson Chunga3195052012-06-25 10:02:10 -07001989 /**
1990 * Updates all providers with the specified package names, and records any providers that were
1991 * pruned.
1992 *
1993 * @return whether any providers were updated
1994 */
1995 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001996 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001997 HashSet<String> keep = new HashSet<String>();
1998 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1999 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07002000 List<ResolveInfo> broadcastReceivers;
2001 try {
2002 broadcastReceivers = mPm.queryIntentReceivers(intent,
2003 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
2004 PackageManager.GET_META_DATA, mUserId);
2005 } catch (RemoteException re) {
2006 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07002007 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07002008 }
Amith Yamasani742a6712011-05-04 14:49:28 -07002009
2010 // add the missing ones and collect which ones to keep
2011 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
2012 for (int i = 0; i < N; i++) {
2013 ResolveInfo ri = broadcastReceivers.get(i);
2014 ActivityInfo ai = ri.activityInfo;
2015 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
2016 continue;
2017 }
2018 if (pkgName.equals(ai.packageName)) {
2019 ComponentName component = new ComponentName(ai.packageName, ai.name);
2020 Provider p = lookupProviderLocked(component);
2021 if (p == null) {
2022 if (addProviderLocked(ri)) {
2023 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07002024 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07002025 }
2026 } else {
2027 Provider parsed = parseProviderInfoXml(component, ri);
2028 if (parsed != null) {
2029 keep.add(ai.name);
2030 // Use the new AppWidgetProviderInfo.
2031 p.info = parsed.info;
2032 // If it's enabled
2033 final int M = p.instances.size();
2034 if (M > 0) {
2035 int[] appWidgetIds = getAppWidgetIds(p);
2036 // Reschedule for the new updatePeriodMillis (don't worry about handling
2037 // it specially if updatePeriodMillis didn't change because we just sent
2038 // an update, and the next one will be updatePeriodMillis from now).
2039 cancelBroadcasts(p);
2040 registerForBroadcastsLocked(p, appWidgetIds);
2041 // If it's currently showing, call back with the new
2042 // AppWidgetProviderInfo.
2043 for (int j = 0; j < M; j++) {
2044 AppWidgetId id = p.instances.get(j);
2045 id.views = null;
2046 if (id.host != null && id.host.callbacks != null) {
2047 try {
Jim Millera75a8832013-02-07 16:53:32 -08002048 id.host.callbacks.providerChanged(id.appWidgetId, p.info,
2049 mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07002050 } catch (RemoteException ex) {
2051 // It failed; remove the callback. No need to prune because
2052 // we know that this host is still referenced by this
2053 // instance.
2054 id.host.callbacks = null;
2055 }
2056 }
2057 }
2058 // Now that we've told the host, push out an update.
2059 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07002060 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07002061 }
2062 }
2063 }
2064 }
2065 }
2066
2067 // prune the ones we don't want to keep
2068 N = mInstalledProviders.size();
2069 for (int i = N - 1; i >= 0; i--) {
2070 Provider p = mInstalledProviders.get(i);
2071 if (pkgName.equals(p.info.provider.getPackageName())
2072 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07002073 if (removedProviders != null) {
2074 removedProviders.add(p.info.provider);
2075 }
Amith Yamasani742a6712011-05-04 14:49:28 -07002076 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07002077 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07002078 }
2079 }
Winson Chung7fbd2842012-06-13 10:35:51 -07002080
2081 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07002082 }
2083
Winson Chung7fbd2842012-06-13 10:35:51 -07002084 boolean removeProvidersForPackageLocked(String pkgName) {
2085 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07002086 int N = mInstalledProviders.size();
2087 for (int i = N - 1; i >= 0; i--) {
2088 Provider p = mInstalledProviders.get(i);
2089 if (pkgName.equals(p.info.provider.getPackageName())) {
2090 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07002091 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07002092 }
2093 }
2094
2095 // Delete the hosts for this package too
2096 //
2097 // By now, we have removed any AppWidgets that were in any hosts here,
2098 // so we don't need to worry about sending DISABLE broadcasts to them.
2099 N = mHosts.size();
2100 for (int i = N - 1; i >= 0; i--) {
2101 Host host = mHosts.get(i);
2102 if (pkgName.equals(host.packageName)) {
2103 deleteHostLocked(host);
2104 }
2105 }
Winson Chung7fbd2842012-06-13 10:35:51 -07002106
2107 return providersRemoved;
2108 }
2109
2110 void notifyHostsForProvidersChangedLocked() {
2111 final int N = mHosts.size();
2112 for (int i = N - 1; i >= 0; i--) {
2113 Host host = mHosts.get(i);
2114 try {
2115 if (host.callbacks != null) {
Jim Millera75a8832013-02-07 16:53:32 -08002116 host.callbacks.providersChanged(mUserId);
Winson Chung7fbd2842012-06-13 10:35:51 -07002117 }
2118 } catch (RemoteException ex) {
2119 // It failed; remove the callback. No need to prune because
2120 // we know that this host is still referenced by this
2121 // instance.
2122 host.callbacks = null;
2123 }
2124 }
Amith Yamasani742a6712011-05-04 14:49:28 -07002125 }
2126}