blob: 58015512c574fff48d6cd7b809ac2d9f651d4db3 [file] [log] [blame]
Amith Yamasani742a6712011-05-04 14:49:28 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.AlarmManager;
Amith Yamasani483f3b02012-03-13 16:08:00 -070020import android.app.AppGlobals;
Amith Yamasani742a6712011-05-04 14:49:28 -070021import android.app.PendingIntent;
22import android.appwidget.AppWidgetManager;
23import android.appwidget.AppWidgetProviderInfo;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
Amith Yamasani742a6712011-05-04 14:49:28 -070027import android.content.Intent.FilterComparison;
Michael Jurka61a5b012012-04-13 10:39:45 -070028import android.content.ServiceConnection;
Amith Yamasani742a6712011-05-04 14:49:28 -070029import android.content.pm.ActivityInfo;
30import android.content.pm.ApplicationInfo;
Amith Yamasani483f3b02012-03-13 16:08:00 -070031import android.content.pm.IPackageManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070032import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.ResolveInfo;
35import android.content.pm.ServiceInfo;
36import android.content.res.Resources;
37import android.content.res.TypedArray;
38import android.content.res.XmlResourceParser;
Jeff Browna8b9def2012-07-23 14:22:49 -070039import android.graphics.Point;
Amith Yamasani742a6712011-05-04 14:49:28 -070040import android.net.Uri;
41import android.os.Binder;
42import android.os.Bundle;
Amith Yamasani61f57372012-08-31 12:12:28 -070043import android.os.Environment;
Amith Yamasani742a6712011-05-04 14:49:28 -070044import android.os.IBinder;
45import android.os.RemoteException;
46import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070047import android.os.UserHandle;
Dianne Hackborn39606a02012-07-31 17:54:35 -070048import android.util.AtomicFile;
Amith Yamasani742a6712011-05-04 14:49:28 -070049import android.util.AttributeSet;
50import android.util.Log;
51import android.util.Pair;
52import android.util.Slog;
53import android.util.TypedValue;
54import android.util.Xml;
Jeff Browna8b9def2012-07-23 14:22:49 -070055import android.view.Display;
Adam Cohen311c79c2012-05-10 14:44:38 -070056import android.view.WindowManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070057import android.widget.RemoteViews;
58
59import com.android.internal.appwidget.IAppWidgetHost;
Amith Yamasani742a6712011-05-04 14:49:28 -070060import com.android.internal.util.FastXmlSerializer;
61import com.android.internal.widget.IRemoteViewsAdapterConnection;
62import com.android.internal.widget.IRemoteViewsFactory;
Amith Yamasani742a6712011-05-04 14:49:28 -070063
64import org.xmlpull.v1.XmlPullParser;
65import org.xmlpull.v1.XmlPullParserException;
66import org.xmlpull.v1.XmlSerializer;
67
68import java.io.File;
69import java.io.FileDescriptor;
70import java.io.FileInputStream;
71import java.io.FileNotFoundException;
72import java.io.FileOutputStream;
73import java.io.IOException;
74import java.io.PrintWriter;
75import java.util.ArrayList;
76import java.util.HashMap;
77import java.util.HashSet;
78import java.util.Iterator;
79import java.util.List;
80import java.util.Locale;
81import java.util.Set;
82
83class AppWidgetServiceImpl {
84
85 private static final String TAG = "AppWidgetServiceImpl";
86 private static final String SETTINGS_FILENAME = "appwidgets.xml";
87 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
88
89 /*
90 * When identifying a Host or Provider based on the calling process, use the uid field. When
91 * identifying a Host or Provider based on a package manager broadcast, use the package given.
92 */
93
94 static class Provider {
95 int uid;
96 AppWidgetProviderInfo info;
97 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
98 PendingIntent broadcast;
99 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
100
101 int tag; // for use while saving state (the index)
102 }
103
104 static class Host {
105 int uid;
106 int hostId;
107 String packageName;
108 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
109 IAppWidgetHost callbacks;
110 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
111
112 int tag; // for use while saving state (the index)
113 }
114
115 static class AppWidgetId {
116 int appWidgetId;
117 Provider provider;
118 RemoteViews views;
Adam Cohend2097eb2012-05-01 18:10:28 -0700119 Bundle options;
Amith Yamasani742a6712011-05-04 14:49:28 -0700120 Host host;
121 }
122
123 /**
124 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
125 * needs to be a static inner class since a reference to the ServiceConnection is held globally
126 * and may lead us to leak AppWidgetService instances (if there were more than one).
127 */
128 static class ServiceConnectionProxy implements ServiceConnection {
129 private final IBinder mConnectionCb;
130
131 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
132 mConnectionCb = connectionCb;
133 }
134
135 public void onServiceConnected(ComponentName name, IBinder service) {
136 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
137 .asInterface(mConnectionCb);
138 try {
139 cb.onServiceConnected(service);
140 } catch (Exception e) {
141 e.printStackTrace();
142 }
143 }
144
145 public void onServiceDisconnected(ComponentName name) {
146 disconnect();
147 }
148
149 public void disconnect() {
150 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
151 .asInterface(mConnectionCb);
152 try {
153 cb.onServiceDisconnected();
154 } catch (Exception e) {
155 e.printStackTrace();
156 }
157 }
158 }
159
160 // Manages active connections to RemoteViewsServices
161 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
162 // Manages persistent references to RemoteViewsServices from different App Widgets
163 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
164
165 Context mContext;
166 Locale mLocale;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700167 IPackageManager mPm;
Amith Yamasani742a6712011-05-04 14:49:28 -0700168 AlarmManager mAlarmManager;
169 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
170 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
171 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
172 ArrayList<Host> mHosts = new ArrayList<Host>();
Michael Jurka61a5b012012-04-13 10:39:45 -0700173 // set of package names
174 HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
Amith Yamasani742a6712011-05-04 14:49:28 -0700175 boolean mSafeMode;
176 int mUserId;
177 boolean mStateLoaded;
Adam Cohen311c79c2012-05-10 14:44:38 -0700178 int mMaxWidgetBitmapMemory;
Amith Yamasani742a6712011-05-04 14:49:28 -0700179
180 // These are for debugging only -- widgets are going missing in some rare instances
181 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
182 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
183
184 AppWidgetServiceImpl(Context context, int userId) {
185 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700186 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700187 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
188 mUserId = userId;
Adam Cohen311c79c2012-05-10 14:44:38 -0700189 computeMaximumWidgetBitmapMemory();
190 }
191
192 void computeMaximumWidgetBitmapMemory() {
193 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Jeff Browna8b9def2012-07-23 14:22:49 -0700194 Display display = wm.getDefaultDisplay();
195 Point size = new Point();
196 display.getRealSize(size);
Winson Chunge92aad42012-06-22 14:11:47 -0700197 // Cap memory usage at 1.5 times the size of the display
198 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
Jeff Browna8b9def2012-07-23 14:22:49 -0700199 mMaxWidgetBitmapMemory = 6 * size.x * size.y;
Amith Yamasani742a6712011-05-04 14:49:28 -0700200 }
201
202 public void systemReady(boolean safeMode) {
203 mSafeMode = safeMode;
204
205 synchronized (mAppWidgetIds) {
206 ensureStateLoadedLocked();
207 }
208 }
209
210 void onConfigurationChanged() {
211 Locale revised = Locale.getDefault();
212 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
213 mLocale = revised;
214
215 synchronized (mAppWidgetIds) {
216 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700217 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
218 // list of installed providers and skip providers that we don't need to update.
219 // Also note that remove the provider does not clear the Provider component data.
220 ArrayList<Provider> installedProviders =
221 new ArrayList<Provider>(mInstalledProviders);
222 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
223 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700224 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700225 Provider p = installedProviders.get(i);
226 ComponentName cn = p.info.provider;
227 if (!removedProviders.contains(cn)) {
228 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
229 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700230 }
231 saveStateLocked();
232 }
233 }
234 }
235
236 void onBroadcastReceived(Intent intent) {
237 final String action = intent.getAction();
238 boolean added = false;
239 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700240 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700241 String pkgList[] = null;
242 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
243 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
244 added = true;
245 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
246 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
247 added = false;
248 } else {
249 Uri uri = intent.getData();
250 if (uri == null) {
251 return;
252 }
253 String pkgName = uri.getSchemeSpecificPart();
254 if (pkgName == null) {
255 return;
256 }
257 pkgList = new String[] { pkgName };
258 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
259 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
260 }
261 if (pkgList == null || pkgList.length == 0) {
262 return;
263 }
264 if (added || changed) {
265 synchronized (mAppWidgetIds) {
266 ensureStateLoadedLocked();
267 Bundle extras = intent.getExtras();
268 if (changed
269 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
270 for (String pkgName : pkgList) {
271 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700272 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700273 }
274 } else {
275 // The package was just added
276 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700277 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700278 }
279 }
280 saveStateLocked();
281 }
282 } else {
283 Bundle extras = intent.getExtras();
284 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
285 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
286 } else {
287 synchronized (mAppWidgetIds) {
288 ensureStateLoadedLocked();
289 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700290 providersModified |= removeProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700291 saveStateLocked();
292 }
293 }
294 }
295 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700296
297 if (providersModified) {
298 // If the set of providers has been modified, notify each active AppWidgetHost
299 synchronized (mAppWidgetIds) {
300 ensureStateLoadedLocked();
301 notifyHostsForProvidersChangedLocked();
302 }
303 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700304 }
305
306 private void dumpProvider(Provider p, int index, PrintWriter pw) {
307 AppWidgetProviderInfo info = p.info;
308 pw.print(" ["); pw.print(index); pw.print("] provider ");
309 pw.print(info.provider.flattenToShortString());
310 pw.println(':');
311 pw.print(" min=("); pw.print(info.minWidth);
312 pw.print("x"); pw.print(info.minHeight);
313 pw.print(") minResize=("); pw.print(info.minResizeWidth);
314 pw.print("x"); pw.print(info.minResizeHeight);
315 pw.print(") updatePeriodMillis=");
316 pw.print(info.updatePeriodMillis);
317 pw.print(" resizeMode=");
318 pw.print(info.resizeMode);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700319 pw.print(info.widgetCategory);
320 pw.print(info.widgetFeatures);
Amith Yamasani742a6712011-05-04 14:49:28 -0700321 pw.print(" autoAdvanceViewId=");
322 pw.print(info.autoAdvanceViewId);
323 pw.print(" initialLayout=#");
324 pw.print(Integer.toHexString(info.initialLayout));
325 pw.print(" zombie="); pw.println(p.zombie);
326 }
327
328 private void dumpHost(Host host, int index, PrintWriter pw) {
329 pw.print(" ["); pw.print(index); pw.print("] hostId=");
330 pw.print(host.hostId); pw.print(' ');
331 pw.print(host.packageName); pw.print('/');
332 pw.print(host.uid); pw.println(':');
333 pw.print(" callbacks="); pw.println(host.callbacks);
334 pw.print(" instances.size="); pw.print(host.instances.size());
335 pw.print(" zombie="); pw.println(host.zombie);
336 }
337
338 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
339 pw.print(" ["); pw.print(index); pw.print("] id=");
340 pw.println(id.appWidgetId);
341 pw.print(" hostId=");
342 pw.print(id.host.hostId); pw.print(' ');
343 pw.print(id.host.packageName); pw.print('/');
344 pw.println(id.host.uid);
345 if (id.provider != null) {
346 pw.print(" provider=");
347 pw.println(id.provider.info.provider.flattenToShortString());
348 }
349 if (id.host != null) {
350 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
351 }
352 if (id.views != null) {
353 pw.print(" views="); pw.println(id.views);
354 }
355 }
356
357 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
358 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
359 != PackageManager.PERMISSION_GRANTED) {
360 pw.println("Permission Denial: can't dump from from pid="
361 + Binder.getCallingPid()
362 + ", uid=" + Binder.getCallingUid());
363 return;
364 }
365
366 synchronized (mAppWidgetIds) {
367 int N = mInstalledProviders.size();
368 pw.println("Providers:");
369 for (int i=0; i<N; i++) {
370 dumpProvider(mInstalledProviders.get(i), i, pw);
371 }
372
373 N = mAppWidgetIds.size();
374 pw.println(" ");
375 pw.println("AppWidgetIds:");
376 for (int i=0; i<N; i++) {
377 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
378 }
379
380 N = mHosts.size();
381 pw.println(" ");
382 pw.println("Hosts:");
383 for (int i=0; i<N; i++) {
384 dumpHost(mHosts.get(i), i, pw);
385 }
386
387 N = mDeletedProviders.size();
388 pw.println(" ");
389 pw.println("Deleted Providers:");
390 for (int i=0; i<N; i++) {
391 dumpProvider(mDeletedProviders.get(i), i, pw);
392 }
393
394 N = mDeletedHosts.size();
395 pw.println(" ");
396 pw.println("Deleted Hosts:");
397 for (int i=0; i<N; i++) {
398 dumpHost(mDeletedHosts.get(i), i, pw);
399 }
400 }
401 }
402
403 private void ensureStateLoadedLocked() {
404 if (!mStateLoaded) {
405 loadAppWidgetList();
406 loadStateLocked();
407 mStateLoaded = true;
408 }
409 }
410
411 public int allocateAppWidgetId(String packageName, int hostId) {
412 int callingUid = enforceCallingUid(packageName);
413 synchronized (mAppWidgetIds) {
414 ensureStateLoadedLocked();
415 int appWidgetId = mNextAppWidgetId++;
416
417 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
418
419 AppWidgetId id = new AppWidgetId();
420 id.appWidgetId = appWidgetId;
421 id.host = host;
422
423 host.instances.add(id);
424 mAppWidgetIds.add(id);
425
426 saveStateLocked();
427
428 return appWidgetId;
429 }
430 }
431
432 public void deleteAppWidgetId(int appWidgetId) {
433 synchronized (mAppWidgetIds) {
434 ensureStateLoadedLocked();
435 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
436 if (id != null) {
437 deleteAppWidgetLocked(id);
438 saveStateLocked();
439 }
440 }
441 }
442
443 public void deleteHost(int hostId) {
444 synchronized (mAppWidgetIds) {
445 ensureStateLoadedLocked();
446 int callingUid = Binder.getCallingUid();
447 Host host = lookupHostLocked(callingUid, hostId);
448 if (host != null) {
449 deleteHostLocked(host);
450 saveStateLocked();
451 }
452 }
453 }
454
455 public void deleteAllHosts() {
456 synchronized (mAppWidgetIds) {
457 ensureStateLoadedLocked();
458 int callingUid = Binder.getCallingUid();
459 final int N = mHosts.size();
460 boolean changed = false;
461 for (int i = N - 1; i >= 0; i--) {
462 Host host = mHosts.get(i);
463 if (host.uid == callingUid) {
464 deleteHostLocked(host);
465 changed = true;
466 }
467 }
468 if (changed) {
469 saveStateLocked();
470 }
471 }
472 }
473
474 void deleteHostLocked(Host host) {
475 final int N = host.instances.size();
476 for (int i = N - 1; i >= 0; i--) {
477 AppWidgetId id = host.instances.get(i);
478 deleteAppWidgetLocked(id);
479 }
480 host.instances.clear();
481 mHosts.remove(host);
482 mDeletedHosts.add(host);
483 // it's gone or going away, abruptly drop the callback connection
484 host.callbacks = null;
485 }
486
487 void deleteAppWidgetLocked(AppWidgetId id) {
488 // We first unbind all services that are bound to this id
489 unbindAppWidgetRemoteViewsServicesLocked(id);
490
491 Host host = id.host;
492 host.instances.remove(id);
493 pruneHostLocked(host);
494
495 mAppWidgetIds.remove(id);
496
497 Provider p = id.provider;
498 if (p != null) {
499 p.instances.remove(id);
500 if (!p.zombie) {
501 // send the broacast saying that this appWidgetId has been deleted
502 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
503 intent.setComponent(p.info.provider);
504 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700505 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700506 if (p.instances.size() == 0) {
507 // cancel the future updates
508 cancelBroadcasts(p);
509
510 // send the broacast saying that the provider is not in use any more
511 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
512 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700513 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700514 }
515 }
516 }
517 }
518
519 void cancelBroadcasts(Provider p) {
520 if (p.broadcast != null) {
521 mAlarmManager.cancel(p.broadcast);
522 long token = Binder.clearCallingIdentity();
523 try {
524 p.broadcast.cancel();
525 } finally {
526 Binder.restoreCallingIdentity(token);
527 }
528 p.broadcast = null;
529 }
530 }
531
Adam Cohen0aa2d422012-09-07 17:37:26 -0700532 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700533 final long ident = Binder.clearCallingIdentity();
534 try {
535 synchronized (mAppWidgetIds) {
536 ensureStateLoadedLocked();
537 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
538 if (id == null) {
539 throw new IllegalArgumentException("bad appWidgetId");
540 }
541 if (id.provider != null) {
542 throw new IllegalArgumentException("appWidgetId " + appWidgetId
543 + " already bound to " + id.provider.info.provider);
544 }
545 Provider p = lookupProviderLocked(provider);
546 if (p == null) {
547 throw new IllegalArgumentException("not a appwidget provider: " + provider);
548 }
549 if (p.zombie) {
550 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
551 + " safe mode: " + provider);
552 }
553
Amith Yamasani742a6712011-05-04 14:49:28 -0700554 id.provider = p;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700555 if (options == null) {
556 options = new Bundle();
557 }
558 id.options = options;
559
560 // We need to provide a default value for the widget category if it is not specified
561 if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
562 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
563 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
564 }
565
Amith Yamasani742a6712011-05-04 14:49:28 -0700566 p.instances.add(id);
567 int instancesSize = p.instances.size();
568 if (instancesSize == 1) {
569 // tell the provider that it's ready
570 sendEnableIntentLocked(p);
571 }
572
573 // send an update now -- We need this update now, and just for this appWidgetId.
574 // It's less critical when the next one happens, so when we schedule the next one,
575 // we add updatePeriodMillis to its start time. That time will have some slop,
576 // but that's okay.
577 sendUpdateIntentLocked(p, new int[] { appWidgetId });
578
579 // schedule the future updates
580 registerForBroadcastsLocked(p, getAppWidgetIds(p));
581 saveStateLocked();
582 }
583 } finally {
584 Binder.restoreCallingIdentity(ident);
585 }
586 }
587
Adam Cohen0aa2d422012-09-07 17:37:26 -0700588 public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700589 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
590 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700591 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700592 }
593
594 public boolean bindAppWidgetIdIfAllowed(
Adam Cohen0aa2d422012-09-07 17:37:26 -0700595 String packageName, int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700596 try {
597 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
598 } catch (SecurityException se) {
599 if (!callerHasBindAppWidgetPermission(packageName)) {
600 return false;
601 }
602 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700603 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700604 return true;
605 }
606
607 private boolean callerHasBindAppWidgetPermission(String packageName) {
608 int callingUid = Binder.getCallingUid();
609 try {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700610 if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700611 return false;
612 }
613 } catch (Exception e) {
614 return false;
615 }
616 synchronized (mAppWidgetIds) {
617 ensureStateLoadedLocked();
618 return mPackagesWithBindWidgetPermission.contains(packageName);
619 }
620 }
621
622 public boolean hasBindAppWidgetPermission(String packageName) {
623 mContext.enforceCallingPermission(
624 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
625 "hasBindAppWidgetPermission packageName=" + packageName);
626
627 synchronized (mAppWidgetIds) {
628 ensureStateLoadedLocked();
629 return mPackagesWithBindWidgetPermission.contains(packageName);
630 }
631 }
632
633 public void setBindAppWidgetPermission(String packageName, boolean permission) {
634 mContext.enforceCallingPermission(
635 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
636 "setBindAppWidgetPermission packageName=" + packageName);
637
638 synchronized (mAppWidgetIds) {
639 ensureStateLoadedLocked();
640 if (permission) {
641 mPackagesWithBindWidgetPermission.add(packageName);
642 } else {
643 mPackagesWithBindWidgetPermission.remove(packageName);
644 }
645 }
646 saveStateLocked();
647 }
648
Amith Yamasani742a6712011-05-04 14:49:28 -0700649 // Binds to a specific RemoteViewsService
650 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
651 synchronized (mAppWidgetIds) {
652 ensureStateLoadedLocked();
653 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
654 if (id == null) {
655 throw new IllegalArgumentException("bad appWidgetId");
656 }
657 final ComponentName componentName = intent.getComponent();
658 try {
659 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
660 PackageManager.GET_PERMISSIONS);
661 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
662 throw new SecurityException("Selected service does not require "
663 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
664 }
665 } catch (PackageManager.NameNotFoundException e) {
666 throw new IllegalArgumentException("Unknown component " + componentName);
667 }
668
669 // If there is already a connection made for this service intent, then disconnect from
670 // that first. (This does not allow multiple connections to the same service under
671 // the same key)
672 ServiceConnectionProxy conn = null;
673 FilterComparison fc = new FilterComparison(intent);
674 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
675 if (mBoundRemoteViewsServices.containsKey(key)) {
676 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
677 conn.disconnect();
678 mContext.unbindService(conn);
679 mBoundRemoteViewsServices.remove(key);
680 }
681
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700682 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700683 // Bind to the RemoteViewsService (which will trigger a callback to the
684 // RemoteViewsAdapter.onServiceConnected())
685 final long token = Binder.clearCallingIdentity();
686 try {
687 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800688 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700689 mBoundRemoteViewsServices.put(key, conn);
690 } finally {
691 Binder.restoreCallingIdentity(token);
692 }
693
694 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
695 // when we can call back to the RemoteViewsService later to destroy associated
696 // factories.
697 incrementAppWidgetServiceRefCount(appWidgetId, fc);
698 }
699 }
700
701 // Unbinds from a specific RemoteViewsService
702 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
703 synchronized (mAppWidgetIds) {
704 ensureStateLoadedLocked();
705 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
706 // RemoteViewsAdapter)
707 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
708 intent));
709 if (mBoundRemoteViewsServices.containsKey(key)) {
710 // We don't need to use the appWidgetId until after we are sure there is something
711 // to unbind. Note that this may mask certain issues with apps calling unbind()
712 // more than necessary.
713 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
714 if (id == null) {
715 throw new IllegalArgumentException("bad appWidgetId");
716 }
717
718 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
719 .get(key);
720 conn.disconnect();
721 mContext.unbindService(conn);
722 mBoundRemoteViewsServices.remove(key);
723 } else {
724 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
725 }
726 }
727 }
728
729 // Unbinds from a RemoteViewsService when we delete an app widget
730 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
731 int appWidgetId = id.appWidgetId;
732 // Unbind all connections to Services bound to this AppWidgetId
733 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
734 .iterator();
735 while (it.hasNext()) {
736 final Pair<Integer, Intent.FilterComparison> key = it.next();
737 if (key.first.intValue() == appWidgetId) {
738 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
739 .get(key);
740 conn.disconnect();
741 mContext.unbindService(conn);
742 it.remove();
743 }
744 }
745
746 // Check if we need to destroy any services (if no other app widgets are
747 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800748 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700749 }
750
751 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800752 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700753 final ServiceConnection conn = new ServiceConnection() {
754 @Override
755 public void onServiceConnected(ComponentName name, IBinder service) {
756 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
757 try {
758 cb.onDestroy(intent);
759 } catch (RemoteException e) {
760 e.printStackTrace();
761 } catch (RuntimeException e) {
762 e.printStackTrace();
763 }
764 mContext.unbindService(this);
765 }
766
767 @Override
768 public void onServiceDisconnected(android.content.ComponentName name) {
769 // Do nothing
770 }
771 };
772
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700773 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700774 // Bind to the service and remove the static intent->factory mapping in the
775 // RemoteViewsService.
776 final long token = Binder.clearCallingIdentity();
777 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800778 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700779 } finally {
780 Binder.restoreCallingIdentity(token);
781 }
782 }
783
784 // Adds to the ref-count for a given RemoteViewsService intent
785 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
786 HashSet<Integer> appWidgetIds = null;
787 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
788 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
789 } else {
790 appWidgetIds = new HashSet<Integer>();
791 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
792 }
793 appWidgetIds.add(appWidgetId);
794 }
795
796 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
797 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800798 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700799 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
800 while (it.hasNext()) {
801 final FilterComparison key = it.next();
802 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800803 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700804 // If we have removed the last app widget referencing this service, then we
805 // should destroy it and remove it from this set
806 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800807 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700808 it.remove();
809 }
810 }
811 }
812 }
813
814 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
815 synchronized (mAppWidgetIds) {
816 ensureStateLoadedLocked();
817 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
818 if (id != null && id.provider != null && !id.provider.zombie) {
819 return id.provider.info;
820 }
821 return null;
822 }
823 }
824
825 public RemoteViews getAppWidgetViews(int appWidgetId) {
826 synchronized (mAppWidgetIds) {
827 ensureStateLoadedLocked();
828 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
829 if (id != null) {
830 return id.views;
831 }
832 return null;
833 }
834 }
835
836 public List<AppWidgetProviderInfo> getInstalledProviders() {
837 synchronized (mAppWidgetIds) {
838 ensureStateLoadedLocked();
839 final int N = mInstalledProviders.size();
840 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
841 for (int i = 0; i < N; i++) {
842 Provider p = mInstalledProviders.get(i);
843 if (!p.zombie) {
844 result.add(p.info);
845 }
846 }
847 return result;
848 }
849 }
850
851 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
852 if (appWidgetIds == null) {
853 return;
854 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700855
Adam Cohenf08a8b72012-07-16 12:02:10 -0700856 int bitmapMemoryUsage = 0;
857 if (views != null) {
858 bitmapMemoryUsage = views.estimateMemoryUsage();
859 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700860 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
861 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
862 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
863 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
864 " fill the device's screen once.");
865 }
866
Amith Yamasani742a6712011-05-04 14:49:28 -0700867 if (appWidgetIds.length == 0) {
868 return;
869 }
870 final int N = appWidgetIds.length;
871
872 synchronized (mAppWidgetIds) {
873 ensureStateLoadedLocked();
874 for (int i = 0; i < N; i++) {
875 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
876 updateAppWidgetInstanceLocked(id, views);
877 }
878 }
879 }
880
Adam Cohend2097eb2012-05-01 18:10:28 -0700881 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700882 synchronized (mAppWidgetIds) {
883 ensureStateLoadedLocked();
884 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
885
886 if (id == null) {
887 return;
888 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700889
Adam Cohene8724c82012-04-19 17:11:40 -0700890 Provider p = id.provider;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700891 // Merge the options
892 id.options.putAll(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700893
894 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700895 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700896 intent.setComponent(p.info.provider);
897 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700898 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700899 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Adam Cohen0aa2d422012-09-07 17:37:26 -0700900 saveStateLocked();
Adam Cohene8724c82012-04-19 17:11:40 -0700901 }
902 }
903
Adam Cohend2097eb2012-05-01 18:10:28 -0700904 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -0700905 synchronized (mAppWidgetIds) {
906 ensureStateLoadedLocked();
907 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700908 if (id != null && id.options != null) {
909 return id.options;
Adam Cohene8724c82012-04-19 17:11:40 -0700910 } else {
911 return Bundle.EMPTY;
912 }
913 }
914 }
915
Amith Yamasani742a6712011-05-04 14:49:28 -0700916 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
917 if (appWidgetIds == null) {
918 return;
919 }
920 if (appWidgetIds.length == 0) {
921 return;
922 }
923 final int N = appWidgetIds.length;
924
925 synchronized (mAppWidgetIds) {
926 ensureStateLoadedLocked();
927 for (int i = 0; i < N; i++) {
928 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
929 updateAppWidgetInstanceLocked(id, views, true);
930 }
931 }
932 }
933
934 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
935 if (appWidgetIds == null) {
936 return;
937 }
938 if (appWidgetIds.length == 0) {
939 return;
940 }
941 final int N = appWidgetIds.length;
942
943 synchronized (mAppWidgetIds) {
944 ensureStateLoadedLocked();
945 for (int i = 0; i < N; i++) {
946 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
947 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
948 }
949 }
950 }
951
952 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
953 synchronized (mAppWidgetIds) {
954 ensureStateLoadedLocked();
955 Provider p = lookupProviderLocked(provider);
956 if (p == null) {
957 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
958 return;
959 }
960 ArrayList<AppWidgetId> instances = p.instances;
961 final int callingUid = Binder.getCallingUid();
962 final int N = instances.size();
963 for (int i = 0; i < N; i++) {
964 AppWidgetId id = instances.get(i);
965 if (canAccessAppWidgetId(id, callingUid)) {
966 updateAppWidgetInstanceLocked(id, views);
967 }
968 }
969 }
970 }
971
972 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
973 updateAppWidgetInstanceLocked(id, views, false);
974 }
975
976 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
977 // allow for stale appWidgetIds and other badness
978 // lookup also checks that the calling process can access the appWidgetId
979 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
980 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
981
982 // We do not want to save this RemoteViews
983 if (!isPartialUpdate)
984 id.views = views;
985
986 // is anyone listening?
987 if (id.host.callbacks != null) {
988 try {
989 // the lock is held, but this is a oneway call
990 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
991 } catch (RemoteException e) {
992 // It failed; remove the callback. No need to prune because
993 // we know that this host is still referenced by this instance.
994 id.host.callbacks = null;
995 }
996 }
997 }
998 }
999
1000 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1001 // allow for stale appWidgetIds and other badness
1002 // lookup also checks that the calling process can access the appWidgetId
1003 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1004 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1005 // is anyone listening?
1006 if (id.host.callbacks != null) {
1007 try {
1008 // the lock is held, but this is a oneway call
1009 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1010 } catch (RemoteException e) {
1011 // It failed; remove the callback. No need to prune because
1012 // we know that this host is still referenced by this instance.
1013 id.host.callbacks = null;
1014 }
1015 }
1016
1017 // If the host is unavailable, then we call the associated
1018 // RemoteViewsFactory.onDataSetChanged() directly
1019 if (id.host.callbacks == null) {
1020 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1021 for (FilterComparison key : keys) {
1022 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1023 Intent intent = key.getIntent();
1024
1025 final ServiceConnection conn = new ServiceConnection() {
1026 @Override
1027 public void onServiceConnected(ComponentName name, IBinder service) {
1028 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1029 .asInterface(service);
1030 try {
1031 cb.onDataSetChangedAsync();
1032 } catch (RemoteException e) {
1033 e.printStackTrace();
1034 } catch (RuntimeException e) {
1035 e.printStackTrace();
1036 }
1037 mContext.unbindService(this);
1038 }
1039
1040 @Override
1041 public void onServiceDisconnected(android.content.ComponentName name) {
1042 // Do nothing
1043 }
1044 };
1045
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001046 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001047 // Bind to the service and call onDataSetChanged()
1048 final long token = Binder.clearCallingIdentity();
1049 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001050 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001051 } finally {
1052 Binder.restoreCallingIdentity(token);
1053 }
1054 }
1055 }
1056 }
1057 }
1058 }
1059
1060 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1061 List<RemoteViews> updatedViews) {
1062 int callingUid = enforceCallingUid(packageName);
1063 synchronized (mAppWidgetIds) {
1064 ensureStateLoadedLocked();
1065 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1066 host.callbacks = callbacks;
1067
1068 updatedViews.clear();
1069
1070 ArrayList<AppWidgetId> instances = host.instances;
1071 int N = instances.size();
1072 int[] updatedIds = new int[N];
1073 for (int i = 0; i < N; i++) {
1074 AppWidgetId id = instances.get(i);
1075 updatedIds[i] = id.appWidgetId;
1076 updatedViews.add(id.views);
1077 }
1078 return updatedIds;
1079 }
1080 }
1081
1082 public void stopListening(int hostId) {
1083 synchronized (mAppWidgetIds) {
1084 ensureStateLoadedLocked();
1085 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1086 if (host != null) {
1087 host.callbacks = null;
1088 pruneHostLocked(host);
1089 }
1090 }
1091 }
1092
1093 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1094 if (id.host.uid == callingUid) {
1095 // Apps hosting the AppWidget have access to it.
1096 return true;
1097 }
1098 if (id.provider != null && id.provider.uid == callingUid) {
1099 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1100 return true;
1101 }
1102 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1103 // Apps that can bind have access to all appWidgetIds.
1104 return true;
1105 }
1106 // Nobody else can access it.
1107 return false;
1108 }
1109
1110 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1111 int callingUid = Binder.getCallingUid();
1112 final int N = mAppWidgetIds.size();
1113 for (int i = 0; i < N; i++) {
1114 AppWidgetId id = mAppWidgetIds.get(i);
1115 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1116 return id;
1117 }
1118 }
1119 return null;
1120 }
1121
1122 Provider lookupProviderLocked(ComponentName provider) {
1123 final int N = mInstalledProviders.size();
1124 for (int i = 0; i < N; i++) {
1125 Provider p = mInstalledProviders.get(i);
1126 if (p.info.provider.equals(provider)) {
1127 return p;
1128 }
1129 }
1130 return null;
1131 }
1132
1133 Host lookupHostLocked(int uid, int hostId) {
1134 final int N = mHosts.size();
1135 for (int i = 0; i < N; i++) {
1136 Host h = mHosts.get(i);
1137 if (h.uid == uid && h.hostId == hostId) {
1138 return h;
1139 }
1140 }
1141 return null;
1142 }
1143
1144 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1145 final int N = mHosts.size();
1146 for (int i = 0; i < N; i++) {
1147 Host h = mHosts.get(i);
1148 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1149 return h;
1150 }
1151 }
1152 Host host = new Host();
1153 host.packageName = packageName;
1154 host.uid = uid;
1155 host.hostId = hostId;
1156 mHosts.add(host);
1157 return host;
1158 }
1159
1160 void pruneHostLocked(Host host) {
1161 if (host.instances.size() == 0 && host.callbacks == null) {
1162 mHosts.remove(host);
1163 }
1164 }
1165
1166 void loadAppWidgetList() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001167 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001168 try {
1169 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1170 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1171 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001172
Amith Yamasani483f3b02012-03-13 16:08:00 -07001173 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1174 for (int i = 0; i < N; i++) {
1175 ResolveInfo ri = broadcastReceivers.get(i);
1176 addProviderLocked(ri);
1177 }
1178 } catch (RemoteException re) {
1179 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001180 }
1181 }
1182
1183 boolean addProviderLocked(ResolveInfo ri) {
1184 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1185 return false;
1186 }
1187 if (!ri.activityInfo.isEnabled()) {
1188 return false;
1189 }
1190 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1191 ri.activityInfo.name), ri);
1192 if (p != null) {
1193 mInstalledProviders.add(p);
1194 return true;
1195 } else {
1196 return false;
1197 }
1198 }
1199
1200 void removeProviderLocked(int index, Provider p) {
1201 int N = p.instances.size();
1202 for (int i = 0; i < N; i++) {
1203 AppWidgetId id = p.instances.get(i);
1204 // Call back with empty RemoteViews
1205 updateAppWidgetInstanceLocked(id, null);
1206 // Stop telling the host about updates for this from now on
1207 cancelBroadcasts(p);
1208 // clear out references to this appWidgetId
1209 id.host.instances.remove(id);
1210 mAppWidgetIds.remove(id);
1211 id.provider = null;
1212 pruneHostLocked(id.host);
1213 id.host = null;
1214 }
1215 p.instances.clear();
1216 mInstalledProviders.remove(index);
1217 mDeletedProviders.add(p);
1218 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1219 cancelBroadcasts(p);
1220 }
1221
1222 void sendEnableIntentLocked(Provider p) {
1223 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1224 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001225 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001226 }
1227
1228 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1229 if (appWidgetIds != null && appWidgetIds.length > 0) {
1230 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1231 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1232 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001233 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001234 }
1235 }
1236
1237 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1238 if (p.info.updatePeriodMillis > 0) {
1239 // if this is the first instance, set the alarm. otherwise,
1240 // rely on the fact that we've already set it and that
1241 // PendingIntent.getBroadcast will update the extras.
1242 boolean alreadyRegistered = p.broadcast != null;
1243 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1244 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1245 intent.setComponent(p.info.provider);
1246 long token = Binder.clearCallingIdentity();
1247 try {
1248 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1249 PendingIntent.FLAG_UPDATE_CURRENT);
1250 } finally {
1251 Binder.restoreCallingIdentity(token);
1252 }
1253 if (!alreadyRegistered) {
1254 long period = p.info.updatePeriodMillis;
1255 if (period < MIN_UPDATE_PERIOD) {
1256 period = MIN_UPDATE_PERIOD;
1257 }
1258 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1259 .elapsedRealtime()
1260 + period, period, p.broadcast);
1261 }
1262 }
1263 }
1264
1265 static int[] getAppWidgetIds(Provider p) {
1266 int instancesSize = p.instances.size();
1267 int appWidgetIds[] = new int[instancesSize];
1268 for (int i = 0; i < instancesSize; i++) {
1269 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1270 }
1271 return appWidgetIds;
1272 }
1273
1274 public int[] getAppWidgetIds(ComponentName provider) {
1275 synchronized (mAppWidgetIds) {
1276 ensureStateLoadedLocked();
1277 Provider p = lookupProviderLocked(provider);
1278 if (p != null && Binder.getCallingUid() == p.uid) {
1279 return getAppWidgetIds(p);
1280 } else {
1281 return new int[0];
1282 }
1283 }
1284 }
1285
1286 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1287 Provider p = null;
1288
1289 ActivityInfo activityInfo = ri.activityInfo;
1290 XmlResourceParser parser = null;
1291 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001292 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001293 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1294 if (parser == null) {
1295 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1296 + " meta-data for " + "AppWidget provider '" + component + '\'');
1297 return null;
1298 }
1299
1300 AttributeSet attrs = Xml.asAttributeSet(parser);
1301
1302 int type;
1303 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1304 && type != XmlPullParser.START_TAG) {
1305 // drain whitespace, comments, etc.
1306 }
1307
1308 String nodeName = parser.getName();
1309 if (!"appwidget-provider".equals(nodeName)) {
1310 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1311 + " AppWidget provider '" + component + '\'');
1312 return null;
1313 }
1314
1315 p = new Provider();
1316 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1317 info.provider = component;
1318 p.uid = activityInfo.applicationInfo.uid;
1319
Amith Yamasani483f3b02012-03-13 16:08:00 -07001320 Resources res = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001321 .getResourcesForApplication(activityInfo.applicationInfo);
1322
1323 TypedArray sa = res.obtainAttributes(attrs,
1324 com.android.internal.R.styleable.AppWidgetProviderInfo);
1325
1326 // These dimensions has to be resolved in the application's context.
1327 // We simply send back the raw complex data, which will be
1328 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1329 TypedValue value = sa
1330 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1331 info.minWidth = value != null ? value.data : 0;
1332 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1333 info.minHeight = value != null ? value.data : 0;
1334 value = sa.peekValue(
1335 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1336 info.minResizeWidth = value != null ? value.data : info.minWidth;
1337 value = sa.peekValue(
1338 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1339 info.minResizeHeight = value != null ? value.data : info.minHeight;
1340 info.updatePeriodMillis = sa.getInt(
1341 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1342 info.initialLayout = sa.getResourceId(
1343 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001344 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1345 AppWidgetProviderInfo_initialKeyguardLayout, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001346 String className = sa
1347 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1348 if (className != null) {
1349 info.configure = new ComponentName(component.getPackageName(), className);
1350 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001351 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001352 info.icon = ri.getIconResource();
1353 info.previewImage = sa.getResourceId(
1354 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1355 info.autoAdvanceViewId = sa.getResourceId(
1356 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1357 info.resizeMode = sa.getInt(
1358 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1359 AppWidgetProviderInfo.RESIZE_NONE);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001360 info.widgetCategory = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001361 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001362 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
1363 info.widgetFeatures = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001364 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001365 AppWidgetProviderInfo.WIDGET_FEATURES_NONE);
Amith Yamasani742a6712011-05-04 14:49:28 -07001366
1367 sa.recycle();
1368 } catch (Exception e) {
1369 // Ok to catch Exception here, because anything going wrong because
1370 // of what a client process passes to us should not be fatal for the
1371 // system process.
1372 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1373 return null;
1374 } finally {
1375 if (parser != null)
1376 parser.close();
1377 }
1378 return p;
1379 }
1380
1381 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001382 PackageInfo pkgInfo = null;
1383 try {
1384 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1385 } catch (RemoteException re) {
1386 // Shouldn't happen, local call
1387 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001388 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1389 throw new PackageManager.NameNotFoundException();
1390 }
1391 return pkgInfo.applicationInfo.uid;
1392 }
1393
1394 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1395 int callingUid = Binder.getCallingUid();
1396 int packageUid;
1397 try {
1398 packageUid = getUidForPackage(packageName);
1399 } catch (PackageManager.NameNotFoundException ex) {
1400 throw new IllegalArgumentException("packageName and uid don't match packageName="
1401 + packageName);
1402 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001403 if (!UserHandle.isSameApp(callingUid, packageUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001404 throw new IllegalArgumentException("packageName and uid don't match packageName="
1405 + packageName);
1406 }
1407 return callingUid;
1408 }
1409
1410 void sendInitialBroadcasts() {
1411 synchronized (mAppWidgetIds) {
1412 ensureStateLoadedLocked();
1413 final int N = mInstalledProviders.size();
1414 for (int i = 0; i < N; i++) {
1415 Provider p = mInstalledProviders.get(i);
1416 if (p.instances.size() > 0) {
1417 sendEnableIntentLocked(p);
1418 int[] appWidgetIds = getAppWidgetIds(p);
1419 sendUpdateIntentLocked(p, appWidgetIds);
1420 registerForBroadcastsLocked(p, appWidgetIds);
1421 }
1422 }
1423 }
1424 }
1425
1426 // only call from initialization -- it assumes that the data structures are all empty
1427 void loadStateLocked() {
1428 AtomicFile file = savedStateFile();
1429 try {
1430 FileInputStream stream = file.openRead();
1431 readStateFromFileLocked(stream);
1432
1433 if (stream != null) {
1434 try {
1435 stream.close();
1436 } catch (IOException e) {
1437 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1438 }
1439 }
1440 } catch (FileNotFoundException e) {
1441 Slog.w(TAG, "Failed to read state: " + e);
1442 }
1443 }
1444
1445 void saveStateLocked() {
1446 AtomicFile file = savedStateFile();
1447 FileOutputStream stream;
1448 try {
1449 stream = file.startWrite();
1450 if (writeStateToFileLocked(stream)) {
1451 file.finishWrite(stream);
1452 } else {
1453 file.failWrite(stream);
1454 Slog.w(TAG, "Failed to save state, restoring backup.");
1455 }
1456 } catch (IOException e) {
1457 Slog.w(TAG, "Failed open state file for write: " + e);
1458 }
1459 }
1460
1461 boolean writeStateToFileLocked(FileOutputStream stream) {
1462 int N;
1463
1464 try {
1465 XmlSerializer out = new FastXmlSerializer();
1466 out.setOutput(stream, "utf-8");
1467 out.startDocument(null, true);
1468 out.startTag(null, "gs");
1469
1470 int providerIndex = 0;
1471 N = mInstalledProviders.size();
1472 for (int i = 0; i < N; i++) {
1473 Provider p = mInstalledProviders.get(i);
1474 if (p.instances.size() > 0) {
1475 out.startTag(null, "p");
1476 out.attribute(null, "pkg", p.info.provider.getPackageName());
1477 out.attribute(null, "cl", p.info.provider.getClassName());
1478 out.endTag(null, "p");
1479 p.tag = providerIndex;
1480 providerIndex++;
1481 }
1482 }
1483
1484 N = mHosts.size();
1485 for (int i = 0; i < N; i++) {
1486 Host host = mHosts.get(i);
1487 out.startTag(null, "h");
1488 out.attribute(null, "pkg", host.packageName);
1489 out.attribute(null, "id", Integer.toHexString(host.hostId));
1490 out.endTag(null, "h");
1491 host.tag = i;
1492 }
1493
1494 N = mAppWidgetIds.size();
1495 for (int i = 0; i < N; i++) {
1496 AppWidgetId id = mAppWidgetIds.get(i);
1497 out.startTag(null, "g");
1498 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1499 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1500 if (id.provider != null) {
1501 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1502 }
Adam Cohen0aa2d422012-09-07 17:37:26 -07001503 if (id.options != null) {
1504 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1505 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1506 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1507 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1508 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1509 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1510 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1511 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1512 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1513 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1514 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001515 out.endTag(null, "g");
1516 }
1517
Michael Jurka61a5b012012-04-13 10:39:45 -07001518 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1519 while (it.hasNext()) {
1520 out.startTag(null, "b");
1521 out.attribute(null, "packageName", it.next());
1522 out.endTag(null, "b");
1523 }
1524
Amith Yamasani742a6712011-05-04 14:49:28 -07001525 out.endTag(null, "gs");
1526
1527 out.endDocument();
1528 return true;
1529 } catch (IOException e) {
1530 Slog.w(TAG, "Failed to write state: " + e);
1531 return false;
1532 }
1533 }
1534
Adam Cohen0aa2d422012-09-07 17:37:26 -07001535 @SuppressWarnings("unused")
Amith Yamasani742a6712011-05-04 14:49:28 -07001536 void readStateFromFileLocked(FileInputStream stream) {
1537 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001538 try {
1539 XmlPullParser parser = Xml.newPullParser();
1540 parser.setInput(stream, null);
1541
1542 int type;
1543 int providerIndex = 0;
1544 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1545 do {
1546 type = parser.next();
1547 if (type == XmlPullParser.START_TAG) {
1548 String tag = parser.getName();
1549 if ("p".equals(tag)) {
1550 // TODO: do we need to check that this package has the same signature
1551 // as before?
1552 String pkg = parser.getAttributeValue(null, "pkg");
1553 String cl = parser.getAttributeValue(null, "cl");
1554
Amith Yamasanif203aee2012-08-29 18:41:53 -07001555 final IPackageManager packageManager = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -07001556 try {
Amith Yamasanif203aee2012-08-29 18:41:53 -07001557 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0,
1558 UserHandle.getCallingUserId());
1559 } catch (RemoteException e) {
1560 String[] pkgs = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001561 .currentToCanonicalPackageNames(new String[] { pkg });
1562 pkg = pkgs[0];
1563 }
1564
1565 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1566 if (p == null && mSafeMode) {
1567 // if we're in safe mode, make a temporary one
1568 p = new Provider();
1569 p.info = new AppWidgetProviderInfo();
1570 p.info.provider = new ComponentName(pkg, cl);
1571 p.zombie = true;
1572 mInstalledProviders.add(p);
1573 }
1574 if (p != null) {
1575 // if it wasn't uninstalled or something
1576 loadedProviders.put(providerIndex, p);
1577 }
1578 providerIndex++;
1579 } else if ("h".equals(tag)) {
1580 Host host = new Host();
1581
1582 // TODO: do we need to check that this package has the same signature
1583 // as before?
1584 host.packageName = parser.getAttributeValue(null, "pkg");
1585 try {
1586 host.uid = getUidForPackage(host.packageName);
1587 } catch (PackageManager.NameNotFoundException ex) {
1588 host.zombie = true;
1589 }
1590 if (!host.zombie || mSafeMode) {
1591 // In safe mode, we don't discard the hosts we don't recognize
1592 // so that they're not pruned from our list. Otherwise, we do.
1593 host.hostId = Integer
1594 .parseInt(parser.getAttributeValue(null, "id"), 16);
1595 mHosts.add(host);
1596 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001597 } else if ("b".equals(tag)) {
1598 String packageName = parser.getAttributeValue(null, "packageName");
1599 if (packageName != null) {
1600 mPackagesWithBindWidgetPermission.add(packageName);
1601 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001602 } else if ("g".equals(tag)) {
1603 AppWidgetId id = new AppWidgetId();
1604 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1605 if (id.appWidgetId >= mNextAppWidgetId) {
1606 mNextAppWidgetId = id.appWidgetId + 1;
1607 }
1608
Adam Cohen0aa2d422012-09-07 17:37:26 -07001609 Bundle options = new Bundle();
1610 String minWidthString = parser.getAttributeValue(null, "min_width");
1611 if (minWidthString != null) {
1612 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1613 Integer.parseInt(minWidthString, 16));
1614 }
1615 String minHeightString = parser.getAttributeValue(null, "min_height");
1616 if (minWidthString != null) {
1617 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1618 Integer.parseInt(minHeightString, 16));
1619 }
1620 String maxWidthString = parser.getAttributeValue(null, "max_height");
1621 if (minWidthString != null) {
1622 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1623 Integer.parseInt(maxWidthString, 16));
1624 }
1625 String maxHeightString = parser.getAttributeValue(null, "max_height");
1626 if (minWidthString != null) {
1627 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1628 Integer.parseInt(maxHeightString, 16));
1629 }
1630 String categoryString = parser.getAttributeValue(null, "host_category");
1631 if (minWidthString != null) {
1632 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1633 Integer.parseInt(categoryString, 16));
1634 }
1635 id.options = options;
1636
Amith Yamasani742a6712011-05-04 14:49:28 -07001637 String providerString = parser.getAttributeValue(null, "p");
1638 if (providerString != null) {
1639 // there's no provider if it hasn't been bound yet.
1640 // maybe we don't have to save this, but it brings the system
1641 // to the state it was in.
1642 int pIndex = Integer.parseInt(providerString, 16);
1643 id.provider = loadedProviders.get(pIndex);
1644 if (false) {
1645 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1646 + pIndex + " which is " + id.provider);
1647 }
1648 if (id.provider == null) {
1649 // This provider is gone. We just let the host figure out
1650 // that this happened when it fails to load it.
1651 continue;
1652 }
1653 }
1654
1655 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1656 id.host = mHosts.get(hIndex);
1657 if (id.host == null) {
1658 // This host is gone.
1659 continue;
1660 }
1661
1662 if (id.provider != null) {
1663 id.provider.instances.add(id);
1664 }
1665 id.host.instances.add(id);
1666 mAppWidgetIds.add(id);
1667 }
1668 }
1669 } while (type != XmlPullParser.END_DOCUMENT);
1670 success = true;
1671 } catch (NullPointerException e) {
1672 Slog.w(TAG, "failed parsing " + e);
1673 } catch (NumberFormatException e) {
1674 Slog.w(TAG, "failed parsing " + e);
1675 } catch (XmlPullParserException e) {
1676 Slog.w(TAG, "failed parsing " + e);
1677 } catch (IOException e) {
1678 Slog.w(TAG, "failed parsing " + e);
1679 } catch (IndexOutOfBoundsException e) {
1680 Slog.w(TAG, "failed parsing " + e);
1681 }
1682
1683 if (success) {
1684 // delete any hosts that didn't manage to get connected (should happen)
1685 // if it matters, they'll be reconnected.
1686 for (int i = mHosts.size() - 1; i >= 0; i--) {
1687 pruneHostLocked(mHosts.get(i));
1688 }
1689 } else {
1690 // failed reading, clean up
1691 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1692
1693 mAppWidgetIds.clear();
1694 mHosts.clear();
1695 final int N = mInstalledProviders.size();
1696 for (int i = 0; i < N; i++) {
1697 mInstalledProviders.get(i).instances.clear();
1698 }
1699 }
1700 }
1701
Amith Yamasani13593602012-03-22 16:16:17 -07001702 static File getSettingsFile(int userId) {
Amith Yamasani61f57372012-08-31 12:12:28 -07001703 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
Amith Yamasani13593602012-03-22 16:16:17 -07001704 }
1705
Amith Yamasani742a6712011-05-04 14:49:28 -07001706 AtomicFile savedStateFile() {
Amith Yamasani61f57372012-08-31 12:12:28 -07001707 File dir = Environment.getUserSystemDirectory(mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001708 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001709 if (!settingsFile.exists() && mUserId == 0) {
1710 if (!dir.exists()) {
1711 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001712 }
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001713 // Migrate old data
1714 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1715 // Method doesn't throw an exception on failure. Ignore any errors
1716 // in moving the file (like non-existence)
1717 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001718 }
1719 return new AtomicFile(settingsFile);
1720 }
1721
Amith Yamasani13593602012-03-22 16:16:17 -07001722 void onUserRemoved() {
1723 // prune the ones we don't want to keep
1724 int N = mInstalledProviders.size();
1725 for (int i = N - 1; i >= 0; i--) {
1726 Provider p = mInstalledProviders.get(i);
1727 cancelBroadcasts(p);
1728 }
1729 getSettingsFile(mUserId).delete();
1730 }
1731
Winson Chung7fbd2842012-06-13 10:35:51 -07001732 boolean addProvidersForPackageLocked(String pkgName) {
1733 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001734 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1735 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001736 List<ResolveInfo> broadcastReceivers;
1737 try {
1738 broadcastReceivers = mPm.queryIntentReceivers(intent,
1739 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1740 PackageManager.GET_META_DATA, mUserId);
1741 } catch (RemoteException re) {
1742 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001743 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001744 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001745 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1746 for (int i = 0; i < N; i++) {
1747 ResolveInfo ri = broadcastReceivers.get(i);
1748 ActivityInfo ai = ri.activityInfo;
1749 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1750 continue;
1751 }
1752 if (pkgName.equals(ai.packageName)) {
1753 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001754 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001755 }
1756 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001757
1758 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001759 }
1760
Winson Chunga3195052012-06-25 10:02:10 -07001761 /**
1762 * Updates all providers with the specified package names, and records any providers that were
1763 * pruned.
1764 *
1765 * @return whether any providers were updated
1766 */
1767 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001768 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001769 HashSet<String> keep = new HashSet<String>();
1770 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1771 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001772 List<ResolveInfo> broadcastReceivers;
1773 try {
1774 broadcastReceivers = mPm.queryIntentReceivers(intent,
1775 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1776 PackageManager.GET_META_DATA, mUserId);
1777 } catch (RemoteException re) {
1778 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001779 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001780 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001781
1782 // add the missing ones and collect which ones to keep
1783 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1784 for (int i = 0; i < N; i++) {
1785 ResolveInfo ri = broadcastReceivers.get(i);
1786 ActivityInfo ai = ri.activityInfo;
1787 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1788 continue;
1789 }
1790 if (pkgName.equals(ai.packageName)) {
1791 ComponentName component = new ComponentName(ai.packageName, ai.name);
1792 Provider p = lookupProviderLocked(component);
1793 if (p == null) {
1794 if (addProviderLocked(ri)) {
1795 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001796 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001797 }
1798 } else {
1799 Provider parsed = parseProviderInfoXml(component, ri);
1800 if (parsed != null) {
1801 keep.add(ai.name);
1802 // Use the new AppWidgetProviderInfo.
1803 p.info = parsed.info;
1804 // If it's enabled
1805 final int M = p.instances.size();
1806 if (M > 0) {
1807 int[] appWidgetIds = getAppWidgetIds(p);
1808 // Reschedule for the new updatePeriodMillis (don't worry about handling
1809 // it specially if updatePeriodMillis didn't change because we just sent
1810 // an update, and the next one will be updatePeriodMillis from now).
1811 cancelBroadcasts(p);
1812 registerForBroadcastsLocked(p, appWidgetIds);
1813 // If it's currently showing, call back with the new
1814 // AppWidgetProviderInfo.
1815 for (int j = 0; j < M; j++) {
1816 AppWidgetId id = p.instances.get(j);
1817 id.views = null;
1818 if (id.host != null && id.host.callbacks != null) {
1819 try {
1820 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1821 } catch (RemoteException ex) {
1822 // It failed; remove the callback. No need to prune because
1823 // we know that this host is still referenced by this
1824 // instance.
1825 id.host.callbacks = null;
1826 }
1827 }
1828 }
1829 // Now that we've told the host, push out an update.
1830 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07001831 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001832 }
1833 }
1834 }
1835 }
1836 }
1837
1838 // prune the ones we don't want to keep
1839 N = mInstalledProviders.size();
1840 for (int i = N - 1; i >= 0; i--) {
1841 Provider p = mInstalledProviders.get(i);
1842 if (pkgName.equals(p.info.provider.getPackageName())
1843 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07001844 if (removedProviders != null) {
1845 removedProviders.add(p.info.provider);
1846 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001847 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001848 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001849 }
1850 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001851
1852 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07001853 }
1854
Winson Chung7fbd2842012-06-13 10:35:51 -07001855 boolean removeProvidersForPackageLocked(String pkgName) {
1856 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001857 int N = mInstalledProviders.size();
1858 for (int i = N - 1; i >= 0; i--) {
1859 Provider p = mInstalledProviders.get(i);
1860 if (pkgName.equals(p.info.provider.getPackageName())) {
1861 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001862 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001863 }
1864 }
1865
1866 // Delete the hosts for this package too
1867 //
1868 // By now, we have removed any AppWidgets that were in any hosts here,
1869 // so we don't need to worry about sending DISABLE broadcasts to them.
1870 N = mHosts.size();
1871 for (int i = N - 1; i >= 0; i--) {
1872 Host host = mHosts.get(i);
1873 if (pkgName.equals(host.packageName)) {
1874 deleteHostLocked(host);
1875 }
1876 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001877
1878 return providersRemoved;
1879 }
1880
1881 void notifyHostsForProvidersChangedLocked() {
1882 final int N = mHosts.size();
1883 for (int i = N - 1; i >= 0; i--) {
1884 Host host = mHosts.get(i);
1885 try {
1886 if (host.callbacks != null) {
1887 host.callbacks.providersChanged();
1888 }
1889 } catch (RemoteException ex) {
1890 // It failed; remove the callback. No need to prune because
1891 // we know that this host is still referenced by this
1892 // instance.
1893 host.callbacks = null;
1894 }
1895 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001896 }
1897}