blob: 250386fa5c3d3b48208b0e98d2cca8272fef35a8 [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;
20import android.app.PendingIntent;
21import android.appwidget.AppWidgetManager;
22import android.appwidget.AppWidgetProviderInfo;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.ServiceConnection;
27import android.content.Intent.FilterComparison;
28import android.content.pm.ActivityInfo;
29import android.content.pm.ApplicationInfo;
30import android.content.pm.PackageInfo;
31import android.content.pm.PackageManager;
32import android.content.pm.ResolveInfo;
33import android.content.pm.ServiceInfo;
34import android.content.res.Resources;
35import android.content.res.TypedArray;
36import android.content.res.XmlResourceParser;
37import android.net.Uri;
38import android.os.Binder;
39import android.os.Bundle;
40import android.os.IBinder;
41import android.os.RemoteException;
42import android.os.SystemClock;
43import android.os.UserId;
44import android.util.AttributeSet;
45import android.util.Log;
46import android.util.Pair;
47import android.util.Slog;
48import android.util.TypedValue;
49import android.util.Xml;
50import android.widget.RemoteViews;
51
52import com.android.internal.appwidget.IAppWidgetHost;
53import com.android.internal.os.AtomicFile;
54import com.android.internal.util.FastXmlSerializer;
55import com.android.internal.widget.IRemoteViewsAdapterConnection;
56import com.android.internal.widget.IRemoteViewsFactory;
57import com.android.server.am.ActivityManagerService;
58
59import org.xmlpull.v1.XmlPullParser;
60import org.xmlpull.v1.XmlPullParserException;
61import org.xmlpull.v1.XmlSerializer;
62
63import java.io.File;
64import java.io.FileDescriptor;
65import java.io.FileInputStream;
66import java.io.FileNotFoundException;
67import java.io.FileOutputStream;
68import java.io.IOException;
69import java.io.PrintWriter;
70import java.util.ArrayList;
71import java.util.HashMap;
72import java.util.HashSet;
73import java.util.Iterator;
74import java.util.List;
75import java.util.Locale;
76import java.util.Set;
77
78class AppWidgetServiceImpl {
79
80 private static final String TAG = "AppWidgetServiceImpl";
81 private static final String SETTINGS_FILENAME = "appwidgets.xml";
82 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
83
84 /*
85 * When identifying a Host or Provider based on the calling process, use the uid field. When
86 * identifying a Host or Provider based on a package manager broadcast, use the package given.
87 */
88
89 static class Provider {
90 int uid;
91 AppWidgetProviderInfo info;
92 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
93 PendingIntent broadcast;
94 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
95
96 int tag; // for use while saving state (the index)
97 }
98
99 static class Host {
100 int uid;
101 int hostId;
102 String packageName;
103 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
104 IAppWidgetHost callbacks;
105 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
106
107 int tag; // for use while saving state (the index)
108 }
109
110 static class AppWidgetId {
111 int appWidgetId;
112 Provider provider;
113 RemoteViews views;
114 Host host;
115 }
116
117 /**
118 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
119 * needs to be a static inner class since a reference to the ServiceConnection is held globally
120 * and may lead us to leak AppWidgetService instances (if there were more than one).
121 */
122 static class ServiceConnectionProxy implements ServiceConnection {
123 private final IBinder mConnectionCb;
124
125 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
126 mConnectionCb = connectionCb;
127 }
128
129 public void onServiceConnected(ComponentName name, IBinder service) {
130 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
131 .asInterface(mConnectionCb);
132 try {
133 cb.onServiceConnected(service);
134 } catch (Exception e) {
135 e.printStackTrace();
136 }
137 }
138
139 public void onServiceDisconnected(ComponentName name) {
140 disconnect();
141 }
142
143 public void disconnect() {
144 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
145 .asInterface(mConnectionCb);
146 try {
147 cb.onServiceDisconnected();
148 } catch (Exception e) {
149 e.printStackTrace();
150 }
151 }
152 }
153
154 // Manages active connections to RemoteViewsServices
155 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
156 // Manages persistent references to RemoteViewsServices from different App Widgets
157 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
158
159 Context mContext;
160 Locale mLocale;
161 PackageManager mPackageManager;
162 AlarmManager mAlarmManager;
163 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
164 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
165 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
166 ArrayList<Host> mHosts = new ArrayList<Host>();
167 boolean mSafeMode;
168 int mUserId;
169 boolean mStateLoaded;
170
171 // These are for debugging only -- widgets are going missing in some rare instances
172 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
173 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
174
175 AppWidgetServiceImpl(Context context, int userId) {
176 mContext = context;
177 mPackageManager = context.getPackageManager();
178 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
179 mUserId = userId;
180 }
181
182 public void systemReady(boolean safeMode) {
183 mSafeMode = safeMode;
184
185 synchronized (mAppWidgetIds) {
186 ensureStateLoadedLocked();
187 }
188 }
189
190 void onConfigurationChanged() {
191 Locale revised = Locale.getDefault();
192 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
193 mLocale = revised;
194
195 synchronized (mAppWidgetIds) {
196 ensureStateLoadedLocked();
197 int N = mInstalledProviders.size();
198 for (int i = N - 1; i >= 0; i--) {
199 Provider p = mInstalledProviders.get(i);
200 String pkgName = p.info.provider.getPackageName();
201 updateProvidersForPackageLocked(pkgName);
202 }
203 saveStateLocked();
204 }
205 }
206 }
207
208 void onBroadcastReceived(Intent intent) {
209 final String action = intent.getAction();
210 boolean added = false;
211 boolean changed = false;
212 String pkgList[] = null;
213 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
214 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
215 added = true;
216 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
217 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
218 added = false;
219 } else {
220 Uri uri = intent.getData();
221 if (uri == null) {
222 return;
223 }
224 String pkgName = uri.getSchemeSpecificPart();
225 if (pkgName == null) {
226 return;
227 }
228 pkgList = new String[] { pkgName };
229 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
230 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
231 }
232 if (pkgList == null || pkgList.length == 0) {
233 return;
234 }
235 if (added || changed) {
236 synchronized (mAppWidgetIds) {
237 ensureStateLoadedLocked();
238 Bundle extras = intent.getExtras();
239 if (changed
240 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
241 for (String pkgName : pkgList) {
242 // The package was just upgraded
243 updateProvidersForPackageLocked(pkgName);
244 }
245 } else {
246 // The package was just added
247 for (String pkgName : pkgList) {
248 addProvidersForPackageLocked(pkgName);
249 }
250 }
251 saveStateLocked();
252 }
253 } else {
254 Bundle extras = intent.getExtras();
255 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
256 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
257 } else {
258 synchronized (mAppWidgetIds) {
259 ensureStateLoadedLocked();
260 for (String pkgName : pkgList) {
261 removeProvidersForPackageLocked(pkgName);
262 saveStateLocked();
263 }
264 }
265 }
266 }
267 }
268
269 private void dumpProvider(Provider p, int index, PrintWriter pw) {
270 AppWidgetProviderInfo info = p.info;
271 pw.print(" ["); pw.print(index); pw.print("] provider ");
272 pw.print(info.provider.flattenToShortString());
273 pw.println(':');
274 pw.print(" min=("); pw.print(info.minWidth);
275 pw.print("x"); pw.print(info.minHeight);
276 pw.print(") minResize=("); pw.print(info.minResizeWidth);
277 pw.print("x"); pw.print(info.minResizeHeight);
278 pw.print(") updatePeriodMillis=");
279 pw.print(info.updatePeriodMillis);
280 pw.print(" resizeMode=");
281 pw.print(info.resizeMode);
282 pw.print(" autoAdvanceViewId=");
283 pw.print(info.autoAdvanceViewId);
284 pw.print(" initialLayout=#");
285 pw.print(Integer.toHexString(info.initialLayout));
286 pw.print(" zombie="); pw.println(p.zombie);
287 }
288
289 private void dumpHost(Host host, int index, PrintWriter pw) {
290 pw.print(" ["); pw.print(index); pw.print("] hostId=");
291 pw.print(host.hostId); pw.print(' ');
292 pw.print(host.packageName); pw.print('/');
293 pw.print(host.uid); pw.println(':');
294 pw.print(" callbacks="); pw.println(host.callbacks);
295 pw.print(" instances.size="); pw.print(host.instances.size());
296 pw.print(" zombie="); pw.println(host.zombie);
297 }
298
299 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
300 pw.print(" ["); pw.print(index); pw.print("] id=");
301 pw.println(id.appWidgetId);
302 pw.print(" hostId=");
303 pw.print(id.host.hostId); pw.print(' ');
304 pw.print(id.host.packageName); pw.print('/');
305 pw.println(id.host.uid);
306 if (id.provider != null) {
307 pw.print(" provider=");
308 pw.println(id.provider.info.provider.flattenToShortString());
309 }
310 if (id.host != null) {
311 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
312 }
313 if (id.views != null) {
314 pw.print(" views="); pw.println(id.views);
315 }
316 }
317
318 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
319 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
320 != PackageManager.PERMISSION_GRANTED) {
321 pw.println("Permission Denial: can't dump from from pid="
322 + Binder.getCallingPid()
323 + ", uid=" + Binder.getCallingUid());
324 return;
325 }
326
327 synchronized (mAppWidgetIds) {
328 int N = mInstalledProviders.size();
329 pw.println("Providers:");
330 for (int i=0; i<N; i++) {
331 dumpProvider(mInstalledProviders.get(i), i, pw);
332 }
333
334 N = mAppWidgetIds.size();
335 pw.println(" ");
336 pw.println("AppWidgetIds:");
337 for (int i=0; i<N; i++) {
338 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
339 }
340
341 N = mHosts.size();
342 pw.println(" ");
343 pw.println("Hosts:");
344 for (int i=0; i<N; i++) {
345 dumpHost(mHosts.get(i), i, pw);
346 }
347
348 N = mDeletedProviders.size();
349 pw.println(" ");
350 pw.println("Deleted Providers:");
351 for (int i=0; i<N; i++) {
352 dumpProvider(mDeletedProviders.get(i), i, pw);
353 }
354
355 N = mDeletedHosts.size();
356 pw.println(" ");
357 pw.println("Deleted Hosts:");
358 for (int i=0; i<N; i++) {
359 dumpHost(mDeletedHosts.get(i), i, pw);
360 }
361 }
362 }
363
364 private void ensureStateLoadedLocked() {
365 if (!mStateLoaded) {
366 loadAppWidgetList();
367 loadStateLocked();
368 mStateLoaded = true;
369 }
370 }
371
372 public int allocateAppWidgetId(String packageName, int hostId) {
373 int callingUid = enforceCallingUid(packageName);
374 synchronized (mAppWidgetIds) {
375 ensureStateLoadedLocked();
376 int appWidgetId = mNextAppWidgetId++;
377
378 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
379
380 AppWidgetId id = new AppWidgetId();
381 id.appWidgetId = appWidgetId;
382 id.host = host;
383
384 host.instances.add(id);
385 mAppWidgetIds.add(id);
386
387 saveStateLocked();
388
389 return appWidgetId;
390 }
391 }
392
393 public void deleteAppWidgetId(int appWidgetId) {
394 synchronized (mAppWidgetIds) {
395 ensureStateLoadedLocked();
396 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
397 if (id != null) {
398 deleteAppWidgetLocked(id);
399 saveStateLocked();
400 }
401 }
402 }
403
404 public void deleteHost(int hostId) {
405 synchronized (mAppWidgetIds) {
406 ensureStateLoadedLocked();
407 int callingUid = Binder.getCallingUid();
408 Host host = lookupHostLocked(callingUid, hostId);
409 if (host != null) {
410 deleteHostLocked(host);
411 saveStateLocked();
412 }
413 }
414 }
415
416 public void deleteAllHosts() {
417 synchronized (mAppWidgetIds) {
418 ensureStateLoadedLocked();
419 int callingUid = Binder.getCallingUid();
420 final int N = mHosts.size();
421 boolean changed = false;
422 for (int i = N - 1; i >= 0; i--) {
423 Host host = mHosts.get(i);
424 if (host.uid == callingUid) {
425 deleteHostLocked(host);
426 changed = true;
427 }
428 }
429 if (changed) {
430 saveStateLocked();
431 }
432 }
433 }
434
435 void deleteHostLocked(Host host) {
436 final int N = host.instances.size();
437 for (int i = N - 1; i >= 0; i--) {
438 AppWidgetId id = host.instances.get(i);
439 deleteAppWidgetLocked(id);
440 }
441 host.instances.clear();
442 mHosts.remove(host);
443 mDeletedHosts.add(host);
444 // it's gone or going away, abruptly drop the callback connection
445 host.callbacks = null;
446 }
447
448 void deleteAppWidgetLocked(AppWidgetId id) {
449 // We first unbind all services that are bound to this id
450 unbindAppWidgetRemoteViewsServicesLocked(id);
451
452 Host host = id.host;
453 host.instances.remove(id);
454 pruneHostLocked(host);
455
456 mAppWidgetIds.remove(id);
457
458 Provider p = id.provider;
459 if (p != null) {
460 p.instances.remove(id);
461 if (!p.zombie) {
462 // send the broacast saying that this appWidgetId has been deleted
463 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
464 intent.setComponent(p.info.provider);
465 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
466 mContext.sendBroadcast(intent);
467 if (p.instances.size() == 0) {
468 // cancel the future updates
469 cancelBroadcasts(p);
470
471 // send the broacast saying that the provider is not in use any more
472 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
473 intent.setComponent(p.info.provider);
474 mContext.sendBroadcast(intent);
475 }
476 }
477 }
478 }
479
480 void cancelBroadcasts(Provider p) {
481 if (p.broadcast != null) {
482 mAlarmManager.cancel(p.broadcast);
483 long token = Binder.clearCallingIdentity();
484 try {
485 p.broadcast.cancel();
486 } finally {
487 Binder.restoreCallingIdentity(token);
488 }
489 p.broadcast = null;
490 }
491 }
492
493 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
494 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
495 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
496
497 final long ident = Binder.clearCallingIdentity();
498 try {
499 synchronized (mAppWidgetIds) {
500 ensureStateLoadedLocked();
501 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
502 if (id == null) {
503 throw new IllegalArgumentException("bad appWidgetId");
504 }
505 if (id.provider != null) {
506 throw new IllegalArgumentException("appWidgetId " + appWidgetId
507 + " already bound to " + id.provider.info.provider);
508 }
509 Provider p = lookupProviderLocked(provider);
510 if (p == null) {
511 throw new IllegalArgumentException("not a appwidget provider: " + provider);
512 }
513 if (p.zombie) {
514 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
515 + " safe mode: " + provider);
516 }
517
518 Binder.restoreCallingIdentity(ident);
519
520 id.provider = p;
521 p.instances.add(id);
522 int instancesSize = p.instances.size();
523 if (instancesSize == 1) {
524 // tell the provider that it's ready
525 sendEnableIntentLocked(p);
526 }
527
528 // send an update now -- We need this update now, and just for this appWidgetId.
529 // It's less critical when the next one happens, so when we schedule the next one,
530 // we add updatePeriodMillis to its start time. That time will have some slop,
531 // but that's okay.
532 sendUpdateIntentLocked(p, new int[] { appWidgetId });
533
534 // schedule the future updates
535 registerForBroadcastsLocked(p, getAppWidgetIds(p));
536 saveStateLocked();
537 }
538 } finally {
539 Binder.restoreCallingIdentity(ident);
540 }
541 }
542
543 // Binds to a specific RemoteViewsService
544 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
545 synchronized (mAppWidgetIds) {
546 ensureStateLoadedLocked();
547 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
548 if (id == null) {
549 throw new IllegalArgumentException("bad appWidgetId");
550 }
551 final ComponentName componentName = intent.getComponent();
552 try {
553 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
554 PackageManager.GET_PERMISSIONS);
555 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
556 throw new SecurityException("Selected service does not require "
557 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
558 }
559 } catch (PackageManager.NameNotFoundException e) {
560 throw new IllegalArgumentException("Unknown component " + componentName);
561 }
562
563 // If there is already a connection made for this service intent, then disconnect from
564 // that first. (This does not allow multiple connections to the same service under
565 // the same key)
566 ServiceConnectionProxy conn = null;
567 FilterComparison fc = new FilterComparison(intent);
568 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
569 if (mBoundRemoteViewsServices.containsKey(key)) {
570 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
571 conn.disconnect();
572 mContext.unbindService(conn);
573 mBoundRemoteViewsServices.remove(key);
574 }
575
576 // Bind to the RemoteViewsService (which will trigger a callback to the
577 // RemoteViewsAdapter.onServiceConnected())
578 final long token = Binder.clearCallingIdentity();
579 try {
580 conn = new ServiceConnectionProxy(key, connection);
581 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
582 mBoundRemoteViewsServices.put(key, conn);
583 } finally {
584 Binder.restoreCallingIdentity(token);
585 }
586
587 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
588 // when we can call back to the RemoteViewsService later to destroy associated
589 // factories.
590 incrementAppWidgetServiceRefCount(appWidgetId, fc);
591 }
592 }
593
594 // Unbinds from a specific RemoteViewsService
595 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
596 synchronized (mAppWidgetIds) {
597 ensureStateLoadedLocked();
598 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
599 // RemoteViewsAdapter)
600 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
601 intent));
602 if (mBoundRemoteViewsServices.containsKey(key)) {
603 // We don't need to use the appWidgetId until after we are sure there is something
604 // to unbind. Note that this may mask certain issues with apps calling unbind()
605 // more than necessary.
606 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
607 if (id == null) {
608 throw new IllegalArgumentException("bad appWidgetId");
609 }
610
611 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
612 .get(key);
613 conn.disconnect();
614 mContext.unbindService(conn);
615 mBoundRemoteViewsServices.remove(key);
616 } else {
617 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
618 }
619 }
620 }
621
622 // Unbinds from a RemoteViewsService when we delete an app widget
623 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
624 int appWidgetId = id.appWidgetId;
625 // Unbind all connections to Services bound to this AppWidgetId
626 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
627 .iterator();
628 while (it.hasNext()) {
629 final Pair<Integer, Intent.FilterComparison> key = it.next();
630 if (key.first.intValue() == appWidgetId) {
631 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
632 .get(key);
633 conn.disconnect();
634 mContext.unbindService(conn);
635 it.remove();
636 }
637 }
638
639 // Check if we need to destroy any services (if no other app widgets are
640 // referencing the same service)
641 decrementAppWidgetServiceRefCount(appWidgetId);
642 }
643
644 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
645 private void destroyRemoteViewsService(final Intent intent) {
646 final ServiceConnection conn = new ServiceConnection() {
647 @Override
648 public void onServiceConnected(ComponentName name, IBinder service) {
649 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
650 try {
651 cb.onDestroy(intent);
652 } catch (RemoteException e) {
653 e.printStackTrace();
654 } catch (RuntimeException e) {
655 e.printStackTrace();
656 }
657 mContext.unbindService(this);
658 }
659
660 @Override
661 public void onServiceDisconnected(android.content.ComponentName name) {
662 // Do nothing
663 }
664 };
665
666 // Bind to the service and remove the static intent->factory mapping in the
667 // RemoteViewsService.
668 final long token = Binder.clearCallingIdentity();
669 try {
670 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
671 } finally {
672 Binder.restoreCallingIdentity(token);
673 }
674 }
675
676 // Adds to the ref-count for a given RemoteViewsService intent
677 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
678 HashSet<Integer> appWidgetIds = null;
679 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
680 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
681 } else {
682 appWidgetIds = new HashSet<Integer>();
683 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
684 }
685 appWidgetIds.add(appWidgetId);
686 }
687
688 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
689 // the ref-count reaches zero.
690 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
691 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
692 while (it.hasNext()) {
693 final FilterComparison key = it.next();
694 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
695 if (ids.remove(appWidgetId)) {
696 // If we have removed the last app widget referencing this service, then we
697 // should destroy it and remove it from this set
698 if (ids.isEmpty()) {
699 destroyRemoteViewsService(key.getIntent());
700 it.remove();
701 }
702 }
703 }
704 }
705
706 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
707 synchronized (mAppWidgetIds) {
708 ensureStateLoadedLocked();
709 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
710 if (id != null && id.provider != null && !id.provider.zombie) {
711 return id.provider.info;
712 }
713 return null;
714 }
715 }
716
717 public RemoteViews getAppWidgetViews(int appWidgetId) {
718 synchronized (mAppWidgetIds) {
719 ensureStateLoadedLocked();
720 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
721 if (id != null) {
722 return id.views;
723 }
724 return null;
725 }
726 }
727
728 public List<AppWidgetProviderInfo> getInstalledProviders() {
729 synchronized (mAppWidgetIds) {
730 ensureStateLoadedLocked();
731 final int N = mInstalledProviders.size();
732 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
733 for (int i = 0; i < N; i++) {
734 Provider p = mInstalledProviders.get(i);
735 if (!p.zombie) {
736 result.add(p.info);
737 }
738 }
739 return result;
740 }
741 }
742
743 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
744 if (appWidgetIds == null) {
745 return;
746 }
747 if (appWidgetIds.length == 0) {
748 return;
749 }
750 final int N = appWidgetIds.length;
751
752 synchronized (mAppWidgetIds) {
753 ensureStateLoadedLocked();
754 for (int i = 0; i < N; i++) {
755 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
756 updateAppWidgetInstanceLocked(id, views);
757 }
758 }
759 }
760
761 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
762 if (appWidgetIds == null) {
763 return;
764 }
765 if (appWidgetIds.length == 0) {
766 return;
767 }
768 final int N = appWidgetIds.length;
769
770 synchronized (mAppWidgetIds) {
771 ensureStateLoadedLocked();
772 for (int i = 0; i < N; i++) {
773 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
774 updateAppWidgetInstanceLocked(id, views, true);
775 }
776 }
777 }
778
779 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
780 if (appWidgetIds == null) {
781 return;
782 }
783 if (appWidgetIds.length == 0) {
784 return;
785 }
786 final int N = appWidgetIds.length;
787
788 synchronized (mAppWidgetIds) {
789 ensureStateLoadedLocked();
790 for (int i = 0; i < N; i++) {
791 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
792 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
793 }
794 }
795 }
796
797 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
798 synchronized (mAppWidgetIds) {
799 ensureStateLoadedLocked();
800 Provider p = lookupProviderLocked(provider);
801 if (p == null) {
802 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
803 return;
804 }
805 ArrayList<AppWidgetId> instances = p.instances;
806 final int callingUid = Binder.getCallingUid();
807 final int N = instances.size();
808 for (int i = 0; i < N; i++) {
809 AppWidgetId id = instances.get(i);
810 if (canAccessAppWidgetId(id, callingUid)) {
811 updateAppWidgetInstanceLocked(id, views);
812 }
813 }
814 }
815 }
816
817 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
818 updateAppWidgetInstanceLocked(id, views, false);
819 }
820
821 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
822 // allow for stale appWidgetIds and other badness
823 // lookup also checks that the calling process can access the appWidgetId
824 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
825 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
826
827 // We do not want to save this RemoteViews
828 if (!isPartialUpdate)
829 id.views = views;
830
831 // is anyone listening?
832 if (id.host.callbacks != null) {
833 try {
834 // the lock is held, but this is a oneway call
835 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
836 } catch (RemoteException e) {
837 // It failed; remove the callback. No need to prune because
838 // we know that this host is still referenced by this instance.
839 id.host.callbacks = null;
840 }
841 }
842 }
843 }
844
845 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
846 // allow for stale appWidgetIds and other badness
847 // lookup also checks that the calling process can access the appWidgetId
848 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
849 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
850 // is anyone listening?
851 if (id.host.callbacks != null) {
852 try {
853 // the lock is held, but this is a oneway call
854 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
855 } catch (RemoteException e) {
856 // It failed; remove the callback. No need to prune because
857 // we know that this host is still referenced by this instance.
858 id.host.callbacks = null;
859 }
860 }
861
862 // If the host is unavailable, then we call the associated
863 // RemoteViewsFactory.onDataSetChanged() directly
864 if (id.host.callbacks == null) {
865 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
866 for (FilterComparison key : keys) {
867 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
868 Intent intent = key.getIntent();
869
870 final ServiceConnection conn = new ServiceConnection() {
871 @Override
872 public void onServiceConnected(ComponentName name, IBinder service) {
873 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
874 .asInterface(service);
875 try {
876 cb.onDataSetChangedAsync();
877 } catch (RemoteException e) {
878 e.printStackTrace();
879 } catch (RuntimeException e) {
880 e.printStackTrace();
881 }
882 mContext.unbindService(this);
883 }
884
885 @Override
886 public void onServiceDisconnected(android.content.ComponentName name) {
887 // Do nothing
888 }
889 };
890
891 // Bind to the service and call onDataSetChanged()
892 final long token = Binder.clearCallingIdentity();
893 try {
894 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
895 } finally {
896 Binder.restoreCallingIdentity(token);
897 }
898 }
899 }
900 }
901 }
902 }
903
904 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
905 List<RemoteViews> updatedViews) {
906 int callingUid = enforceCallingUid(packageName);
907 synchronized (mAppWidgetIds) {
908 ensureStateLoadedLocked();
909 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
910 host.callbacks = callbacks;
911
912 updatedViews.clear();
913
914 ArrayList<AppWidgetId> instances = host.instances;
915 int N = instances.size();
916 int[] updatedIds = new int[N];
917 for (int i = 0; i < N; i++) {
918 AppWidgetId id = instances.get(i);
919 updatedIds[i] = id.appWidgetId;
920 updatedViews.add(id.views);
921 }
922 return updatedIds;
923 }
924 }
925
926 public void stopListening(int hostId) {
927 synchronized (mAppWidgetIds) {
928 ensureStateLoadedLocked();
929 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
930 if (host != null) {
931 host.callbacks = null;
932 pruneHostLocked(host);
933 }
934 }
935 }
936
937 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
938 if (id.host.uid == callingUid) {
939 // Apps hosting the AppWidget have access to it.
940 return true;
941 }
942 if (id.provider != null && id.provider.uid == callingUid) {
943 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
944 return true;
945 }
946 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
947 // Apps that can bind have access to all appWidgetIds.
948 return true;
949 }
950 // Nobody else can access it.
951 return false;
952 }
953
954 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
955 int callingUid = Binder.getCallingUid();
956 final int N = mAppWidgetIds.size();
957 for (int i = 0; i < N; i++) {
958 AppWidgetId id = mAppWidgetIds.get(i);
959 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
960 return id;
961 }
962 }
963 return null;
964 }
965
966 Provider lookupProviderLocked(ComponentName provider) {
967 final int N = mInstalledProviders.size();
968 for (int i = 0; i < N; i++) {
969 Provider p = mInstalledProviders.get(i);
970 if (p.info.provider.equals(provider)) {
971 return p;
972 }
973 }
974 return null;
975 }
976
977 Host lookupHostLocked(int uid, int hostId) {
978 final int N = mHosts.size();
979 for (int i = 0; i < N; i++) {
980 Host h = mHosts.get(i);
981 if (h.uid == uid && h.hostId == hostId) {
982 return h;
983 }
984 }
985 return null;
986 }
987
988 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
989 final int N = mHosts.size();
990 for (int i = 0; i < N; i++) {
991 Host h = mHosts.get(i);
992 if (h.hostId == hostId && h.packageName.equals(packageName)) {
993 return h;
994 }
995 }
996 Host host = new Host();
997 host.packageName = packageName;
998 host.uid = uid;
999 host.hostId = hostId;
1000 mHosts.add(host);
1001 return host;
1002 }
1003
1004 void pruneHostLocked(Host host) {
1005 if (host.instances.size() == 0 && host.callbacks == null) {
1006 mHosts.remove(host);
1007 }
1008 }
1009
1010 void loadAppWidgetList() {
1011 PackageManager pm = mPackageManager;
1012
1013 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1014 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
1015 PackageManager.GET_META_DATA);
1016
1017 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1018 for (int i = 0; i < N; i++) {
1019 ResolveInfo ri = broadcastReceivers.get(i);
1020 addProviderLocked(ri);
1021 }
1022 }
1023
1024 boolean addProviderLocked(ResolveInfo ri) {
1025 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1026 return false;
1027 }
1028 if (!ri.activityInfo.isEnabled()) {
1029 return false;
1030 }
1031 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1032 ri.activityInfo.name), ri);
1033 if (p != null) {
1034 mInstalledProviders.add(p);
1035 return true;
1036 } else {
1037 return false;
1038 }
1039 }
1040
1041 void removeProviderLocked(int index, Provider p) {
1042 int N = p.instances.size();
1043 for (int i = 0; i < N; i++) {
1044 AppWidgetId id = p.instances.get(i);
1045 // Call back with empty RemoteViews
1046 updateAppWidgetInstanceLocked(id, null);
1047 // Stop telling the host about updates for this from now on
1048 cancelBroadcasts(p);
1049 // clear out references to this appWidgetId
1050 id.host.instances.remove(id);
1051 mAppWidgetIds.remove(id);
1052 id.provider = null;
1053 pruneHostLocked(id.host);
1054 id.host = null;
1055 }
1056 p.instances.clear();
1057 mInstalledProviders.remove(index);
1058 mDeletedProviders.add(p);
1059 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1060 cancelBroadcasts(p);
1061 }
1062
1063 void sendEnableIntentLocked(Provider p) {
1064 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1065 intent.setComponent(p.info.provider);
1066 mContext.sendBroadcast(intent);
1067 }
1068
1069 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1070 if (appWidgetIds != null && appWidgetIds.length > 0) {
1071 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1072 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1073 intent.setComponent(p.info.provider);
1074 mContext.sendBroadcast(intent);
1075 }
1076 }
1077
1078 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1079 if (p.info.updatePeriodMillis > 0) {
1080 // if this is the first instance, set the alarm. otherwise,
1081 // rely on the fact that we've already set it and that
1082 // PendingIntent.getBroadcast will update the extras.
1083 boolean alreadyRegistered = p.broadcast != null;
1084 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1085 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1086 intent.setComponent(p.info.provider);
1087 long token = Binder.clearCallingIdentity();
1088 try {
1089 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1090 PendingIntent.FLAG_UPDATE_CURRENT);
1091 } finally {
1092 Binder.restoreCallingIdentity(token);
1093 }
1094 if (!alreadyRegistered) {
1095 long period = p.info.updatePeriodMillis;
1096 if (period < MIN_UPDATE_PERIOD) {
1097 period = MIN_UPDATE_PERIOD;
1098 }
1099 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1100 .elapsedRealtime()
1101 + period, period, p.broadcast);
1102 }
1103 }
1104 }
1105
1106 static int[] getAppWidgetIds(Provider p) {
1107 int instancesSize = p.instances.size();
1108 int appWidgetIds[] = new int[instancesSize];
1109 for (int i = 0; i < instancesSize; i++) {
1110 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1111 }
1112 return appWidgetIds;
1113 }
1114
1115 public int[] getAppWidgetIds(ComponentName provider) {
1116 synchronized (mAppWidgetIds) {
1117 ensureStateLoadedLocked();
1118 Provider p = lookupProviderLocked(provider);
1119 if (p != null && Binder.getCallingUid() == p.uid) {
1120 return getAppWidgetIds(p);
1121 } else {
1122 return new int[0];
1123 }
1124 }
1125 }
1126
1127 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1128 Provider p = null;
1129
1130 ActivityInfo activityInfo = ri.activityInfo;
1131 XmlResourceParser parser = null;
1132 try {
1133 parser = activityInfo.loadXmlMetaData(mPackageManager,
1134 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1135 if (parser == null) {
1136 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1137 + " meta-data for " + "AppWidget provider '" + component + '\'');
1138 return null;
1139 }
1140
1141 AttributeSet attrs = Xml.asAttributeSet(parser);
1142
1143 int type;
1144 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1145 && type != XmlPullParser.START_TAG) {
1146 // drain whitespace, comments, etc.
1147 }
1148
1149 String nodeName = parser.getName();
1150 if (!"appwidget-provider".equals(nodeName)) {
1151 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1152 + " AppWidget provider '" + component + '\'');
1153 return null;
1154 }
1155
1156 p = new Provider();
1157 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1158 info.provider = component;
1159 p.uid = activityInfo.applicationInfo.uid;
1160
1161 Resources res = mPackageManager
1162 .getResourcesForApplication(activityInfo.applicationInfo);
1163
1164 TypedArray sa = res.obtainAttributes(attrs,
1165 com.android.internal.R.styleable.AppWidgetProviderInfo);
1166
1167 // These dimensions has to be resolved in the application's context.
1168 // We simply send back the raw complex data, which will be
1169 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1170 TypedValue value = sa
1171 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1172 info.minWidth = value != null ? value.data : 0;
1173 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1174 info.minHeight = value != null ? value.data : 0;
1175 value = sa.peekValue(
1176 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1177 info.minResizeWidth = value != null ? value.data : info.minWidth;
1178 value = sa.peekValue(
1179 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1180 info.minResizeHeight = value != null ? value.data : info.minHeight;
1181 info.updatePeriodMillis = sa.getInt(
1182 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1183 info.initialLayout = sa.getResourceId(
1184 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1185 String className = sa
1186 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1187 if (className != null) {
1188 info.configure = new ComponentName(component.getPackageName(), className);
1189 }
1190 info.label = activityInfo.loadLabel(mPackageManager).toString();
1191 info.icon = ri.getIconResource();
1192 info.previewImage = sa.getResourceId(
1193 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1194 info.autoAdvanceViewId = sa.getResourceId(
1195 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1196 info.resizeMode = sa.getInt(
1197 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1198 AppWidgetProviderInfo.RESIZE_NONE);
1199
1200 sa.recycle();
1201 } catch (Exception e) {
1202 // Ok to catch Exception here, because anything going wrong because
1203 // of what a client process passes to us should not be fatal for the
1204 // system process.
1205 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1206 return null;
1207 } finally {
1208 if (parser != null)
1209 parser.close();
1210 }
1211 return p;
1212 }
1213
1214 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1215 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1216 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1217 throw new PackageManager.NameNotFoundException();
1218 }
1219 return pkgInfo.applicationInfo.uid;
1220 }
1221
1222 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1223 int callingUid = Binder.getCallingUid();
1224 int packageUid;
1225 try {
1226 packageUid = getUidForPackage(packageName);
1227 } catch (PackageManager.NameNotFoundException ex) {
1228 throw new IllegalArgumentException("packageName and uid don't match packageName="
1229 + packageName);
1230 }
1231 if (!UserId.isSameApp(callingUid, packageUid)) {
1232 throw new IllegalArgumentException("packageName and uid don't match packageName="
1233 + packageName);
1234 }
1235 return callingUid;
1236 }
1237
1238 void sendInitialBroadcasts() {
1239 synchronized (mAppWidgetIds) {
1240 ensureStateLoadedLocked();
1241 final int N = mInstalledProviders.size();
1242 for (int i = 0; i < N; i++) {
1243 Provider p = mInstalledProviders.get(i);
1244 if (p.instances.size() > 0) {
1245 sendEnableIntentLocked(p);
1246 int[] appWidgetIds = getAppWidgetIds(p);
1247 sendUpdateIntentLocked(p, appWidgetIds);
1248 registerForBroadcastsLocked(p, appWidgetIds);
1249 }
1250 }
1251 }
1252 }
1253
1254 // only call from initialization -- it assumes that the data structures are all empty
1255 void loadStateLocked() {
1256 AtomicFile file = savedStateFile();
1257 try {
1258 FileInputStream stream = file.openRead();
1259 readStateFromFileLocked(stream);
1260
1261 if (stream != null) {
1262 try {
1263 stream.close();
1264 } catch (IOException e) {
1265 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1266 }
1267 }
1268 } catch (FileNotFoundException e) {
1269 Slog.w(TAG, "Failed to read state: " + e);
1270 }
1271 }
1272
1273 void saveStateLocked() {
1274 AtomicFile file = savedStateFile();
1275 FileOutputStream stream;
1276 try {
1277 stream = file.startWrite();
1278 if (writeStateToFileLocked(stream)) {
1279 file.finishWrite(stream);
1280 } else {
1281 file.failWrite(stream);
1282 Slog.w(TAG, "Failed to save state, restoring backup.");
1283 }
1284 } catch (IOException e) {
1285 Slog.w(TAG, "Failed open state file for write: " + e);
1286 }
1287 }
1288
1289 boolean writeStateToFileLocked(FileOutputStream stream) {
1290 int N;
1291
1292 try {
1293 XmlSerializer out = new FastXmlSerializer();
1294 out.setOutput(stream, "utf-8");
1295 out.startDocument(null, true);
1296 out.startTag(null, "gs");
1297
1298 int providerIndex = 0;
1299 N = mInstalledProviders.size();
1300 for (int i = 0; i < N; i++) {
1301 Provider p = mInstalledProviders.get(i);
1302 if (p.instances.size() > 0) {
1303 out.startTag(null, "p");
1304 out.attribute(null, "pkg", p.info.provider.getPackageName());
1305 out.attribute(null, "cl", p.info.provider.getClassName());
1306 out.endTag(null, "p");
1307 p.tag = providerIndex;
1308 providerIndex++;
1309 }
1310 }
1311
1312 N = mHosts.size();
1313 for (int i = 0; i < N; i++) {
1314 Host host = mHosts.get(i);
1315 out.startTag(null, "h");
1316 out.attribute(null, "pkg", host.packageName);
1317 out.attribute(null, "id", Integer.toHexString(host.hostId));
1318 out.endTag(null, "h");
1319 host.tag = i;
1320 }
1321
1322 N = mAppWidgetIds.size();
1323 for (int i = 0; i < N; i++) {
1324 AppWidgetId id = mAppWidgetIds.get(i);
1325 out.startTag(null, "g");
1326 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1327 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1328 if (id.provider != null) {
1329 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1330 }
1331 out.endTag(null, "g");
1332 }
1333
1334 out.endTag(null, "gs");
1335
1336 out.endDocument();
1337 return true;
1338 } catch (IOException e) {
1339 Slog.w(TAG, "Failed to write state: " + e);
1340 return false;
1341 }
1342 }
1343
1344 void readStateFromFileLocked(FileInputStream stream) {
1345 boolean success = false;
1346
1347 try {
1348 XmlPullParser parser = Xml.newPullParser();
1349 parser.setInput(stream, null);
1350
1351 int type;
1352 int providerIndex = 0;
1353 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1354 do {
1355 type = parser.next();
1356 if (type == XmlPullParser.START_TAG) {
1357 String tag = parser.getName();
1358 if ("p".equals(tag)) {
1359 // TODO: do we need to check that this package has the same signature
1360 // as before?
1361 String pkg = parser.getAttributeValue(null, "pkg");
1362 String cl = parser.getAttributeValue(null, "cl");
1363
1364 final PackageManager packageManager = mContext.getPackageManager();
1365 try {
1366 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1367 } catch (PackageManager.NameNotFoundException e) {
1368 String[] pkgs = packageManager
1369 .currentToCanonicalPackageNames(new String[] { pkg });
1370 pkg = pkgs[0];
1371 }
1372
1373 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1374 if (p == null && mSafeMode) {
1375 // if we're in safe mode, make a temporary one
1376 p = new Provider();
1377 p.info = new AppWidgetProviderInfo();
1378 p.info.provider = new ComponentName(pkg, cl);
1379 p.zombie = true;
1380 mInstalledProviders.add(p);
1381 }
1382 if (p != null) {
1383 // if it wasn't uninstalled or something
1384 loadedProviders.put(providerIndex, p);
1385 }
1386 providerIndex++;
1387 } else if ("h".equals(tag)) {
1388 Host host = new Host();
1389
1390 // TODO: do we need to check that this package has the same signature
1391 // as before?
1392 host.packageName = parser.getAttributeValue(null, "pkg");
1393 try {
1394 host.uid = getUidForPackage(host.packageName);
1395 } catch (PackageManager.NameNotFoundException ex) {
1396 host.zombie = true;
1397 }
1398 if (!host.zombie || mSafeMode) {
1399 // In safe mode, we don't discard the hosts we don't recognize
1400 // so that they're not pruned from our list. Otherwise, we do.
1401 host.hostId = Integer
1402 .parseInt(parser.getAttributeValue(null, "id"), 16);
1403 mHosts.add(host);
1404 }
1405 } else if ("g".equals(tag)) {
1406 AppWidgetId id = new AppWidgetId();
1407 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1408 if (id.appWidgetId >= mNextAppWidgetId) {
1409 mNextAppWidgetId = id.appWidgetId + 1;
1410 }
1411
1412 String providerString = parser.getAttributeValue(null, "p");
1413 if (providerString != null) {
1414 // there's no provider if it hasn't been bound yet.
1415 // maybe we don't have to save this, but it brings the system
1416 // to the state it was in.
1417 int pIndex = Integer.parseInt(providerString, 16);
1418 id.provider = loadedProviders.get(pIndex);
1419 if (false) {
1420 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1421 + pIndex + " which is " + id.provider);
1422 }
1423 if (id.provider == null) {
1424 // This provider is gone. We just let the host figure out
1425 // that this happened when it fails to load it.
1426 continue;
1427 }
1428 }
1429
1430 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1431 id.host = mHosts.get(hIndex);
1432 if (id.host == null) {
1433 // This host is gone.
1434 continue;
1435 }
1436
1437 if (id.provider != null) {
1438 id.provider.instances.add(id);
1439 }
1440 id.host.instances.add(id);
1441 mAppWidgetIds.add(id);
1442 }
1443 }
1444 } while (type != XmlPullParser.END_DOCUMENT);
1445 success = true;
1446 } catch (NullPointerException e) {
1447 Slog.w(TAG, "failed parsing " + e);
1448 } catch (NumberFormatException e) {
1449 Slog.w(TAG, "failed parsing " + e);
1450 } catch (XmlPullParserException e) {
1451 Slog.w(TAG, "failed parsing " + e);
1452 } catch (IOException e) {
1453 Slog.w(TAG, "failed parsing " + e);
1454 } catch (IndexOutOfBoundsException e) {
1455 Slog.w(TAG, "failed parsing " + e);
1456 }
1457
1458 if (success) {
1459 // delete any hosts that didn't manage to get connected (should happen)
1460 // if it matters, they'll be reconnected.
1461 for (int i = mHosts.size() - 1; i >= 0; i--) {
1462 pruneHostLocked(mHosts.get(i));
1463 }
1464 } else {
1465 // failed reading, clean up
1466 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1467
1468 mAppWidgetIds.clear();
1469 mHosts.clear();
1470 final int N = mInstalledProviders.size();
1471 for (int i = 0; i < N; i++) {
1472 mInstalledProviders.get(i).instances.clear();
1473 }
1474 }
1475 }
1476
1477 AtomicFile savedStateFile() {
1478 int userId = Binder.getOrigCallingUser();
1479 File dir = new File("/data/system/users/" + userId);
1480 File settingsFile = new File(dir, SETTINGS_FILENAME);
1481 if (!dir.exists()) {
1482 dir.mkdirs();
1483 if (userId == 0) {
1484 // Migrate old data
1485 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1486 // Method doesn't throw an exception on failure. Ignore any errors
1487 // in moving the file (like non-existence)
1488 oldFile.renameTo(settingsFile);
1489 }
1490 }
1491 return new AtomicFile(settingsFile);
1492 }
1493
1494 void addProvidersForPackageLocked(String pkgName) {
1495 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1496 intent.setPackage(pkgName);
1497 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1498 PackageManager.GET_META_DATA);
1499
1500 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1501 for (int i = 0; i < N; i++) {
1502 ResolveInfo ri = broadcastReceivers.get(i);
1503 ActivityInfo ai = ri.activityInfo;
1504 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1505 continue;
1506 }
1507 if (pkgName.equals(ai.packageName)) {
1508 addProviderLocked(ri);
1509 }
1510 }
1511 }
1512
1513 void updateProvidersForPackageLocked(String pkgName) {
1514 HashSet<String> keep = new HashSet<String>();
1515 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1516 intent.setPackage(pkgName);
1517 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1518 PackageManager.GET_META_DATA);
1519
1520 // add the missing ones and collect which ones to keep
1521 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1522 for (int i = 0; i < N; i++) {
1523 ResolveInfo ri = broadcastReceivers.get(i);
1524 ActivityInfo ai = ri.activityInfo;
1525 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1526 continue;
1527 }
1528 if (pkgName.equals(ai.packageName)) {
1529 ComponentName component = new ComponentName(ai.packageName, ai.name);
1530 Provider p = lookupProviderLocked(component);
1531 if (p == null) {
1532 if (addProviderLocked(ri)) {
1533 keep.add(ai.name);
1534 }
1535 } else {
1536 Provider parsed = parseProviderInfoXml(component, ri);
1537 if (parsed != null) {
1538 keep.add(ai.name);
1539 // Use the new AppWidgetProviderInfo.
1540 p.info = parsed.info;
1541 // If it's enabled
1542 final int M = p.instances.size();
1543 if (M > 0) {
1544 int[] appWidgetIds = getAppWidgetIds(p);
1545 // Reschedule for the new updatePeriodMillis (don't worry about handling
1546 // it specially if updatePeriodMillis didn't change because we just sent
1547 // an update, and the next one will be updatePeriodMillis from now).
1548 cancelBroadcasts(p);
1549 registerForBroadcastsLocked(p, appWidgetIds);
1550 // If it's currently showing, call back with the new
1551 // AppWidgetProviderInfo.
1552 for (int j = 0; j < M; j++) {
1553 AppWidgetId id = p.instances.get(j);
1554 id.views = null;
1555 if (id.host != null && id.host.callbacks != null) {
1556 try {
1557 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1558 } catch (RemoteException ex) {
1559 // It failed; remove the callback. No need to prune because
1560 // we know that this host is still referenced by this
1561 // instance.
1562 id.host.callbacks = null;
1563 }
1564 }
1565 }
1566 // Now that we've told the host, push out an update.
1567 sendUpdateIntentLocked(p, appWidgetIds);
1568 }
1569 }
1570 }
1571 }
1572 }
1573
1574 // prune the ones we don't want to keep
1575 N = mInstalledProviders.size();
1576 for (int i = N - 1; i >= 0; i--) {
1577 Provider p = mInstalledProviders.get(i);
1578 if (pkgName.equals(p.info.provider.getPackageName())
1579 && !keep.contains(p.info.provider.getClassName())) {
1580 removeProviderLocked(i, p);
1581 }
1582 }
1583 }
1584
1585 void removeProvidersForPackageLocked(String pkgName) {
1586 int N = mInstalledProviders.size();
1587 for (int i = N - 1; i >= 0; i--) {
1588 Provider p = mInstalledProviders.get(i);
1589 if (pkgName.equals(p.info.provider.getPackageName())) {
1590 removeProviderLocked(i, p);
1591 }
1592 }
1593
1594 // Delete the hosts for this package too
1595 //
1596 // By now, we have removed any AppWidgets that were in any hosts here,
1597 // so we don't need to worry about sending DISABLE broadcasts to them.
1598 N = mHosts.size();
1599 for (int i = N - 1; i >= 0; i--) {
1600 Host host = mHosts.get(i);
1601 if (pkgName.equals(host.packageName)) {
1602 deleteHostLocked(host);
1603 }
1604 }
1605 }
1606}