blob: 41ede2e4b6086307ecf9864175e5bdf4a24ad293 [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
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800576 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700577 // Bind to the RemoteViewsService (which will trigger a callback to the
578 // RemoteViewsAdapter.onServiceConnected())
579 final long token = Binder.clearCallingIdentity();
580 try {
581 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800582 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700583 mBoundRemoteViewsServices.put(key, conn);
584 } finally {
585 Binder.restoreCallingIdentity(token);
586 }
587
588 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
589 // when we can call back to the RemoteViewsService later to destroy associated
590 // factories.
591 incrementAppWidgetServiceRefCount(appWidgetId, fc);
592 }
593 }
594
595 // Unbinds from a specific RemoteViewsService
596 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
597 synchronized (mAppWidgetIds) {
598 ensureStateLoadedLocked();
599 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
600 // RemoteViewsAdapter)
601 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
602 intent));
603 if (mBoundRemoteViewsServices.containsKey(key)) {
604 // We don't need to use the appWidgetId until after we are sure there is something
605 // to unbind. Note that this may mask certain issues with apps calling unbind()
606 // more than necessary.
607 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
608 if (id == null) {
609 throw new IllegalArgumentException("bad appWidgetId");
610 }
611
612 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
613 .get(key);
614 conn.disconnect();
615 mContext.unbindService(conn);
616 mBoundRemoteViewsServices.remove(key);
617 } else {
618 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
619 }
620 }
621 }
622
623 // Unbinds from a RemoteViewsService when we delete an app widget
624 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
625 int appWidgetId = id.appWidgetId;
626 // Unbind all connections to Services bound to this AppWidgetId
627 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
628 .iterator();
629 while (it.hasNext()) {
630 final Pair<Integer, Intent.FilterComparison> key = it.next();
631 if (key.first.intValue() == appWidgetId) {
632 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
633 .get(key);
634 conn.disconnect();
635 mContext.unbindService(conn);
636 it.remove();
637 }
638 }
639
640 // Check if we need to destroy any services (if no other app widgets are
641 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800642 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700643 }
644
645 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800646 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700647 final ServiceConnection conn = new ServiceConnection() {
648 @Override
649 public void onServiceConnected(ComponentName name, IBinder service) {
650 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
651 try {
652 cb.onDestroy(intent);
653 } catch (RemoteException e) {
654 e.printStackTrace();
655 } catch (RuntimeException e) {
656 e.printStackTrace();
657 }
658 mContext.unbindService(this);
659 }
660
661 @Override
662 public void onServiceDisconnected(android.content.ComponentName name) {
663 // Do nothing
664 }
665 };
666
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800667 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700668 // Bind to the service and remove the static intent->factory mapping in the
669 // RemoteViewsService.
670 final long token = Binder.clearCallingIdentity();
671 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800672 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700673 } finally {
674 Binder.restoreCallingIdentity(token);
675 }
676 }
677
678 // Adds to the ref-count for a given RemoteViewsService intent
679 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
680 HashSet<Integer> appWidgetIds = null;
681 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
682 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
683 } else {
684 appWidgetIds = new HashSet<Integer>();
685 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
686 }
687 appWidgetIds.add(appWidgetId);
688 }
689
690 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
691 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800692 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700693 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
694 while (it.hasNext()) {
695 final FilterComparison key = it.next();
696 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800697 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700698 // If we have removed the last app widget referencing this service, then we
699 // should destroy it and remove it from this set
700 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800701 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700702 it.remove();
703 }
704 }
705 }
706 }
707
708 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
709 synchronized (mAppWidgetIds) {
710 ensureStateLoadedLocked();
711 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
712 if (id != null && id.provider != null && !id.provider.zombie) {
713 return id.provider.info;
714 }
715 return null;
716 }
717 }
718
719 public RemoteViews getAppWidgetViews(int appWidgetId) {
720 synchronized (mAppWidgetIds) {
721 ensureStateLoadedLocked();
722 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
723 if (id != null) {
724 return id.views;
725 }
726 return null;
727 }
728 }
729
730 public List<AppWidgetProviderInfo> getInstalledProviders() {
731 synchronized (mAppWidgetIds) {
732 ensureStateLoadedLocked();
733 final int N = mInstalledProviders.size();
734 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
735 for (int i = 0; i < N; i++) {
736 Provider p = mInstalledProviders.get(i);
737 if (!p.zombie) {
738 result.add(p.info);
739 }
740 }
741 return result;
742 }
743 }
744
745 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
746 if (appWidgetIds == null) {
747 return;
748 }
749 if (appWidgetIds.length == 0) {
750 return;
751 }
752 final int N = appWidgetIds.length;
753
754 synchronized (mAppWidgetIds) {
755 ensureStateLoadedLocked();
756 for (int i = 0; i < N; i++) {
757 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
758 updateAppWidgetInstanceLocked(id, views);
759 }
760 }
761 }
762
763 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
764 if (appWidgetIds == null) {
765 return;
766 }
767 if (appWidgetIds.length == 0) {
768 return;
769 }
770 final int N = appWidgetIds.length;
771
772 synchronized (mAppWidgetIds) {
773 ensureStateLoadedLocked();
774 for (int i = 0; i < N; i++) {
775 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
776 updateAppWidgetInstanceLocked(id, views, true);
777 }
778 }
779 }
780
781 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
782 if (appWidgetIds == null) {
783 return;
784 }
785 if (appWidgetIds.length == 0) {
786 return;
787 }
788 final int N = appWidgetIds.length;
789
790 synchronized (mAppWidgetIds) {
791 ensureStateLoadedLocked();
792 for (int i = 0; i < N; i++) {
793 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
794 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
795 }
796 }
797 }
798
799 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
800 synchronized (mAppWidgetIds) {
801 ensureStateLoadedLocked();
802 Provider p = lookupProviderLocked(provider);
803 if (p == null) {
804 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
805 return;
806 }
807 ArrayList<AppWidgetId> instances = p.instances;
808 final int callingUid = Binder.getCallingUid();
809 final int N = instances.size();
810 for (int i = 0; i < N; i++) {
811 AppWidgetId id = instances.get(i);
812 if (canAccessAppWidgetId(id, callingUid)) {
813 updateAppWidgetInstanceLocked(id, views);
814 }
815 }
816 }
817 }
818
819 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
820 updateAppWidgetInstanceLocked(id, views, false);
821 }
822
823 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
824 // allow for stale appWidgetIds and other badness
825 // lookup also checks that the calling process can access the appWidgetId
826 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
827 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
828
829 // We do not want to save this RemoteViews
830 if (!isPartialUpdate)
831 id.views = views;
832
833 // is anyone listening?
834 if (id.host.callbacks != null) {
835 try {
836 // the lock is held, but this is a oneway call
837 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
838 } catch (RemoteException e) {
839 // It failed; remove the callback. No need to prune because
840 // we know that this host is still referenced by this instance.
841 id.host.callbacks = null;
842 }
843 }
844 }
845 }
846
847 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
848 // allow for stale appWidgetIds and other badness
849 // lookup also checks that the calling process can access the appWidgetId
850 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
851 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
852 // is anyone listening?
853 if (id.host.callbacks != null) {
854 try {
855 // the lock is held, but this is a oneway call
856 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
857 } catch (RemoteException e) {
858 // It failed; remove the callback. No need to prune because
859 // we know that this host is still referenced by this instance.
860 id.host.callbacks = null;
861 }
862 }
863
864 // If the host is unavailable, then we call the associated
865 // RemoteViewsFactory.onDataSetChanged() directly
866 if (id.host.callbacks == null) {
867 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
868 for (FilterComparison key : keys) {
869 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
870 Intent intent = key.getIntent();
871
872 final ServiceConnection conn = new ServiceConnection() {
873 @Override
874 public void onServiceConnected(ComponentName name, IBinder service) {
875 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
876 .asInterface(service);
877 try {
878 cb.onDataSetChangedAsync();
879 } catch (RemoteException e) {
880 e.printStackTrace();
881 } catch (RuntimeException e) {
882 e.printStackTrace();
883 }
884 mContext.unbindService(this);
885 }
886
887 @Override
888 public void onServiceDisconnected(android.content.ComponentName name) {
889 // Do nothing
890 }
891 };
892
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800893 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700894 // Bind to the service and call onDataSetChanged()
895 final long token = Binder.clearCallingIdentity();
896 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800897 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700898 } finally {
899 Binder.restoreCallingIdentity(token);
900 }
901 }
902 }
903 }
904 }
905 }
906
907 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
908 List<RemoteViews> updatedViews) {
909 int callingUid = enforceCallingUid(packageName);
910 synchronized (mAppWidgetIds) {
911 ensureStateLoadedLocked();
912 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
913 host.callbacks = callbacks;
914
915 updatedViews.clear();
916
917 ArrayList<AppWidgetId> instances = host.instances;
918 int N = instances.size();
919 int[] updatedIds = new int[N];
920 for (int i = 0; i < N; i++) {
921 AppWidgetId id = instances.get(i);
922 updatedIds[i] = id.appWidgetId;
923 updatedViews.add(id.views);
924 }
925 return updatedIds;
926 }
927 }
928
929 public void stopListening(int hostId) {
930 synchronized (mAppWidgetIds) {
931 ensureStateLoadedLocked();
932 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
933 if (host != null) {
934 host.callbacks = null;
935 pruneHostLocked(host);
936 }
937 }
938 }
939
940 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
941 if (id.host.uid == callingUid) {
942 // Apps hosting the AppWidget have access to it.
943 return true;
944 }
945 if (id.provider != null && id.provider.uid == callingUid) {
946 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
947 return true;
948 }
949 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
950 // Apps that can bind have access to all appWidgetIds.
951 return true;
952 }
953 // Nobody else can access it.
954 return false;
955 }
956
957 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
958 int callingUid = Binder.getCallingUid();
959 final int N = mAppWidgetIds.size();
960 for (int i = 0; i < N; i++) {
961 AppWidgetId id = mAppWidgetIds.get(i);
962 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
963 return id;
964 }
965 }
966 return null;
967 }
968
969 Provider lookupProviderLocked(ComponentName provider) {
970 final int N = mInstalledProviders.size();
971 for (int i = 0; i < N; i++) {
972 Provider p = mInstalledProviders.get(i);
973 if (p.info.provider.equals(provider)) {
974 return p;
975 }
976 }
977 return null;
978 }
979
980 Host lookupHostLocked(int uid, int hostId) {
981 final int N = mHosts.size();
982 for (int i = 0; i < N; i++) {
983 Host h = mHosts.get(i);
984 if (h.uid == uid && h.hostId == hostId) {
985 return h;
986 }
987 }
988 return null;
989 }
990
991 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
992 final int N = mHosts.size();
993 for (int i = 0; i < N; i++) {
994 Host h = mHosts.get(i);
995 if (h.hostId == hostId && h.packageName.equals(packageName)) {
996 return h;
997 }
998 }
999 Host host = new Host();
1000 host.packageName = packageName;
1001 host.uid = uid;
1002 host.hostId = hostId;
1003 mHosts.add(host);
1004 return host;
1005 }
1006
1007 void pruneHostLocked(Host host) {
1008 if (host.instances.size() == 0 && host.callbacks == null) {
1009 mHosts.remove(host);
1010 }
1011 }
1012
1013 void loadAppWidgetList() {
1014 PackageManager pm = mPackageManager;
1015
1016 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1017 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
1018 PackageManager.GET_META_DATA);
1019
1020 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1021 for (int i = 0; i < N; i++) {
1022 ResolveInfo ri = broadcastReceivers.get(i);
1023 addProviderLocked(ri);
1024 }
1025 }
1026
1027 boolean addProviderLocked(ResolveInfo ri) {
1028 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1029 return false;
1030 }
1031 if (!ri.activityInfo.isEnabled()) {
1032 return false;
1033 }
1034 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1035 ri.activityInfo.name), ri);
1036 if (p != null) {
1037 mInstalledProviders.add(p);
1038 return true;
1039 } else {
1040 return false;
1041 }
1042 }
1043
1044 void removeProviderLocked(int index, Provider p) {
1045 int N = p.instances.size();
1046 for (int i = 0; i < N; i++) {
1047 AppWidgetId id = p.instances.get(i);
1048 // Call back with empty RemoteViews
1049 updateAppWidgetInstanceLocked(id, null);
1050 // Stop telling the host about updates for this from now on
1051 cancelBroadcasts(p);
1052 // clear out references to this appWidgetId
1053 id.host.instances.remove(id);
1054 mAppWidgetIds.remove(id);
1055 id.provider = null;
1056 pruneHostLocked(id.host);
1057 id.host = null;
1058 }
1059 p.instances.clear();
1060 mInstalledProviders.remove(index);
1061 mDeletedProviders.add(p);
1062 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1063 cancelBroadcasts(p);
1064 }
1065
1066 void sendEnableIntentLocked(Provider p) {
1067 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1068 intent.setComponent(p.info.provider);
1069 mContext.sendBroadcast(intent);
1070 }
1071
1072 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1073 if (appWidgetIds != null && appWidgetIds.length > 0) {
1074 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1075 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1076 intent.setComponent(p.info.provider);
1077 mContext.sendBroadcast(intent);
1078 }
1079 }
1080
1081 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1082 if (p.info.updatePeriodMillis > 0) {
1083 // if this is the first instance, set the alarm. otherwise,
1084 // rely on the fact that we've already set it and that
1085 // PendingIntent.getBroadcast will update the extras.
1086 boolean alreadyRegistered = p.broadcast != null;
1087 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1088 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1089 intent.setComponent(p.info.provider);
1090 long token = Binder.clearCallingIdentity();
1091 try {
1092 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1093 PendingIntent.FLAG_UPDATE_CURRENT);
1094 } finally {
1095 Binder.restoreCallingIdentity(token);
1096 }
1097 if (!alreadyRegistered) {
1098 long period = p.info.updatePeriodMillis;
1099 if (period < MIN_UPDATE_PERIOD) {
1100 period = MIN_UPDATE_PERIOD;
1101 }
1102 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1103 .elapsedRealtime()
1104 + period, period, p.broadcast);
1105 }
1106 }
1107 }
1108
1109 static int[] getAppWidgetIds(Provider p) {
1110 int instancesSize = p.instances.size();
1111 int appWidgetIds[] = new int[instancesSize];
1112 for (int i = 0; i < instancesSize; i++) {
1113 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1114 }
1115 return appWidgetIds;
1116 }
1117
1118 public int[] getAppWidgetIds(ComponentName provider) {
1119 synchronized (mAppWidgetIds) {
1120 ensureStateLoadedLocked();
1121 Provider p = lookupProviderLocked(provider);
1122 if (p != null && Binder.getCallingUid() == p.uid) {
1123 return getAppWidgetIds(p);
1124 } else {
1125 return new int[0];
1126 }
1127 }
1128 }
1129
1130 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1131 Provider p = null;
1132
1133 ActivityInfo activityInfo = ri.activityInfo;
1134 XmlResourceParser parser = null;
1135 try {
1136 parser = activityInfo.loadXmlMetaData(mPackageManager,
1137 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1138 if (parser == null) {
1139 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1140 + " meta-data for " + "AppWidget provider '" + component + '\'');
1141 return null;
1142 }
1143
1144 AttributeSet attrs = Xml.asAttributeSet(parser);
1145
1146 int type;
1147 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1148 && type != XmlPullParser.START_TAG) {
1149 // drain whitespace, comments, etc.
1150 }
1151
1152 String nodeName = parser.getName();
1153 if (!"appwidget-provider".equals(nodeName)) {
1154 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1155 + " AppWidget provider '" + component + '\'');
1156 return null;
1157 }
1158
1159 p = new Provider();
1160 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1161 info.provider = component;
1162 p.uid = activityInfo.applicationInfo.uid;
1163
1164 Resources res = mPackageManager
1165 .getResourcesForApplication(activityInfo.applicationInfo);
1166
1167 TypedArray sa = res.obtainAttributes(attrs,
1168 com.android.internal.R.styleable.AppWidgetProviderInfo);
1169
1170 // These dimensions has to be resolved in the application's context.
1171 // We simply send back the raw complex data, which will be
1172 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1173 TypedValue value = sa
1174 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1175 info.minWidth = value != null ? value.data : 0;
1176 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1177 info.minHeight = value != null ? value.data : 0;
1178 value = sa.peekValue(
1179 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1180 info.minResizeWidth = value != null ? value.data : info.minWidth;
1181 value = sa.peekValue(
1182 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1183 info.minResizeHeight = value != null ? value.data : info.minHeight;
1184 info.updatePeriodMillis = sa.getInt(
1185 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1186 info.initialLayout = sa.getResourceId(
1187 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1188 String className = sa
1189 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1190 if (className != null) {
1191 info.configure = new ComponentName(component.getPackageName(), className);
1192 }
1193 info.label = activityInfo.loadLabel(mPackageManager).toString();
1194 info.icon = ri.getIconResource();
1195 info.previewImage = sa.getResourceId(
1196 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1197 info.autoAdvanceViewId = sa.getResourceId(
1198 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1199 info.resizeMode = sa.getInt(
1200 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1201 AppWidgetProviderInfo.RESIZE_NONE);
1202
1203 sa.recycle();
1204 } catch (Exception e) {
1205 // Ok to catch Exception here, because anything going wrong because
1206 // of what a client process passes to us should not be fatal for the
1207 // system process.
1208 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1209 return null;
1210 } finally {
1211 if (parser != null)
1212 parser.close();
1213 }
1214 return p;
1215 }
1216
1217 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1218 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1219 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1220 throw new PackageManager.NameNotFoundException();
1221 }
1222 return pkgInfo.applicationInfo.uid;
1223 }
1224
1225 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1226 int callingUid = Binder.getCallingUid();
1227 int packageUid;
1228 try {
1229 packageUid = getUidForPackage(packageName);
1230 } catch (PackageManager.NameNotFoundException ex) {
1231 throw new IllegalArgumentException("packageName and uid don't match packageName="
1232 + packageName);
1233 }
1234 if (!UserId.isSameApp(callingUid, packageUid)) {
1235 throw new IllegalArgumentException("packageName and uid don't match packageName="
1236 + packageName);
1237 }
1238 return callingUid;
1239 }
1240
1241 void sendInitialBroadcasts() {
1242 synchronized (mAppWidgetIds) {
1243 ensureStateLoadedLocked();
1244 final int N = mInstalledProviders.size();
1245 for (int i = 0; i < N; i++) {
1246 Provider p = mInstalledProviders.get(i);
1247 if (p.instances.size() > 0) {
1248 sendEnableIntentLocked(p);
1249 int[] appWidgetIds = getAppWidgetIds(p);
1250 sendUpdateIntentLocked(p, appWidgetIds);
1251 registerForBroadcastsLocked(p, appWidgetIds);
1252 }
1253 }
1254 }
1255 }
1256
1257 // only call from initialization -- it assumes that the data structures are all empty
1258 void loadStateLocked() {
1259 AtomicFile file = savedStateFile();
1260 try {
1261 FileInputStream stream = file.openRead();
1262 readStateFromFileLocked(stream);
1263
1264 if (stream != null) {
1265 try {
1266 stream.close();
1267 } catch (IOException e) {
1268 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1269 }
1270 }
1271 } catch (FileNotFoundException e) {
1272 Slog.w(TAG, "Failed to read state: " + e);
1273 }
1274 }
1275
1276 void saveStateLocked() {
1277 AtomicFile file = savedStateFile();
1278 FileOutputStream stream;
1279 try {
1280 stream = file.startWrite();
1281 if (writeStateToFileLocked(stream)) {
1282 file.finishWrite(stream);
1283 } else {
1284 file.failWrite(stream);
1285 Slog.w(TAG, "Failed to save state, restoring backup.");
1286 }
1287 } catch (IOException e) {
1288 Slog.w(TAG, "Failed open state file for write: " + e);
1289 }
1290 }
1291
1292 boolean writeStateToFileLocked(FileOutputStream stream) {
1293 int N;
1294
1295 try {
1296 XmlSerializer out = new FastXmlSerializer();
1297 out.setOutput(stream, "utf-8");
1298 out.startDocument(null, true);
1299 out.startTag(null, "gs");
1300
1301 int providerIndex = 0;
1302 N = mInstalledProviders.size();
1303 for (int i = 0; i < N; i++) {
1304 Provider p = mInstalledProviders.get(i);
1305 if (p.instances.size() > 0) {
1306 out.startTag(null, "p");
1307 out.attribute(null, "pkg", p.info.provider.getPackageName());
1308 out.attribute(null, "cl", p.info.provider.getClassName());
1309 out.endTag(null, "p");
1310 p.tag = providerIndex;
1311 providerIndex++;
1312 }
1313 }
1314
1315 N = mHosts.size();
1316 for (int i = 0; i < N; i++) {
1317 Host host = mHosts.get(i);
1318 out.startTag(null, "h");
1319 out.attribute(null, "pkg", host.packageName);
1320 out.attribute(null, "id", Integer.toHexString(host.hostId));
1321 out.endTag(null, "h");
1322 host.tag = i;
1323 }
1324
1325 N = mAppWidgetIds.size();
1326 for (int i = 0; i < N; i++) {
1327 AppWidgetId id = mAppWidgetIds.get(i);
1328 out.startTag(null, "g");
1329 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1330 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1331 if (id.provider != null) {
1332 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1333 }
1334 out.endTag(null, "g");
1335 }
1336
1337 out.endTag(null, "gs");
1338
1339 out.endDocument();
1340 return true;
1341 } catch (IOException e) {
1342 Slog.w(TAG, "Failed to write state: " + e);
1343 return false;
1344 }
1345 }
1346
1347 void readStateFromFileLocked(FileInputStream stream) {
1348 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001349 try {
1350 XmlPullParser parser = Xml.newPullParser();
1351 parser.setInput(stream, null);
1352
1353 int type;
1354 int providerIndex = 0;
1355 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1356 do {
1357 type = parser.next();
1358 if (type == XmlPullParser.START_TAG) {
1359 String tag = parser.getName();
1360 if ("p".equals(tag)) {
1361 // TODO: do we need to check that this package has the same signature
1362 // as before?
1363 String pkg = parser.getAttributeValue(null, "pkg");
1364 String cl = parser.getAttributeValue(null, "cl");
1365
1366 final PackageManager packageManager = mContext.getPackageManager();
1367 try {
1368 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1369 } catch (PackageManager.NameNotFoundException e) {
1370 String[] pkgs = packageManager
1371 .currentToCanonicalPackageNames(new String[] { pkg });
1372 pkg = pkgs[0];
1373 }
1374
1375 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1376 if (p == null && mSafeMode) {
1377 // if we're in safe mode, make a temporary one
1378 p = new Provider();
1379 p.info = new AppWidgetProviderInfo();
1380 p.info.provider = new ComponentName(pkg, cl);
1381 p.zombie = true;
1382 mInstalledProviders.add(p);
1383 }
1384 if (p != null) {
1385 // if it wasn't uninstalled or something
1386 loadedProviders.put(providerIndex, p);
1387 }
1388 providerIndex++;
1389 } else if ("h".equals(tag)) {
1390 Host host = new Host();
1391
1392 // TODO: do we need to check that this package has the same signature
1393 // as before?
1394 host.packageName = parser.getAttributeValue(null, "pkg");
1395 try {
1396 host.uid = getUidForPackage(host.packageName);
1397 } catch (PackageManager.NameNotFoundException ex) {
1398 host.zombie = true;
1399 }
1400 if (!host.zombie || mSafeMode) {
1401 // In safe mode, we don't discard the hosts we don't recognize
1402 // so that they're not pruned from our list. Otherwise, we do.
1403 host.hostId = Integer
1404 .parseInt(parser.getAttributeValue(null, "id"), 16);
1405 mHosts.add(host);
1406 }
1407 } else if ("g".equals(tag)) {
1408 AppWidgetId id = new AppWidgetId();
1409 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1410 if (id.appWidgetId >= mNextAppWidgetId) {
1411 mNextAppWidgetId = id.appWidgetId + 1;
1412 }
1413
1414 String providerString = parser.getAttributeValue(null, "p");
1415 if (providerString != null) {
1416 // there's no provider if it hasn't been bound yet.
1417 // maybe we don't have to save this, but it brings the system
1418 // to the state it was in.
1419 int pIndex = Integer.parseInt(providerString, 16);
1420 id.provider = loadedProviders.get(pIndex);
1421 if (false) {
1422 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1423 + pIndex + " which is " + id.provider);
1424 }
1425 if (id.provider == null) {
1426 // This provider is gone. We just let the host figure out
1427 // that this happened when it fails to load it.
1428 continue;
1429 }
1430 }
1431
1432 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1433 id.host = mHosts.get(hIndex);
1434 if (id.host == null) {
1435 // This host is gone.
1436 continue;
1437 }
1438
1439 if (id.provider != null) {
1440 id.provider.instances.add(id);
1441 }
1442 id.host.instances.add(id);
1443 mAppWidgetIds.add(id);
1444 }
1445 }
1446 } while (type != XmlPullParser.END_DOCUMENT);
1447 success = true;
1448 } catch (NullPointerException e) {
1449 Slog.w(TAG, "failed parsing " + e);
1450 } catch (NumberFormatException e) {
1451 Slog.w(TAG, "failed parsing " + e);
1452 } catch (XmlPullParserException e) {
1453 Slog.w(TAG, "failed parsing " + e);
1454 } catch (IOException e) {
1455 Slog.w(TAG, "failed parsing " + e);
1456 } catch (IndexOutOfBoundsException e) {
1457 Slog.w(TAG, "failed parsing " + e);
1458 }
1459
1460 if (success) {
1461 // delete any hosts that didn't manage to get connected (should happen)
1462 // if it matters, they'll be reconnected.
1463 for (int i = mHosts.size() - 1; i >= 0; i--) {
1464 pruneHostLocked(mHosts.get(i));
1465 }
1466 } else {
1467 // failed reading, clean up
1468 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1469
1470 mAppWidgetIds.clear();
1471 mHosts.clear();
1472 final int N = mInstalledProviders.size();
1473 for (int i = 0; i < N; i++) {
1474 mInstalledProviders.get(i).instances.clear();
1475 }
1476 }
1477 }
1478
1479 AtomicFile savedStateFile() {
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001480 int userId = UserId.getCallingUserId();
Amith Yamasani742a6712011-05-04 14:49:28 -07001481 File dir = new File("/data/system/users/" + userId);
1482 File settingsFile = new File(dir, SETTINGS_FILENAME);
1483 if (!dir.exists()) {
1484 dir.mkdirs();
1485 if (userId == 0) {
1486 // Migrate old data
1487 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1488 // Method doesn't throw an exception on failure. Ignore any errors
1489 // in moving the file (like non-existence)
1490 oldFile.renameTo(settingsFile);
1491 }
1492 }
1493 return new AtomicFile(settingsFile);
1494 }
1495
1496 void addProvidersForPackageLocked(String pkgName) {
1497 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1498 intent.setPackage(pkgName);
1499 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1500 PackageManager.GET_META_DATA);
1501
1502 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1503 for (int i = 0; i < N; i++) {
1504 ResolveInfo ri = broadcastReceivers.get(i);
1505 ActivityInfo ai = ri.activityInfo;
1506 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1507 continue;
1508 }
1509 if (pkgName.equals(ai.packageName)) {
1510 addProviderLocked(ri);
1511 }
1512 }
1513 }
1514
1515 void updateProvidersForPackageLocked(String pkgName) {
1516 HashSet<String> keep = new HashSet<String>();
1517 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1518 intent.setPackage(pkgName);
1519 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1520 PackageManager.GET_META_DATA);
1521
1522 // add the missing ones and collect which ones to keep
1523 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1524 for (int i = 0; i < N; i++) {
1525 ResolveInfo ri = broadcastReceivers.get(i);
1526 ActivityInfo ai = ri.activityInfo;
1527 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1528 continue;
1529 }
1530 if (pkgName.equals(ai.packageName)) {
1531 ComponentName component = new ComponentName(ai.packageName, ai.name);
1532 Provider p = lookupProviderLocked(component);
1533 if (p == null) {
1534 if (addProviderLocked(ri)) {
1535 keep.add(ai.name);
1536 }
1537 } else {
1538 Provider parsed = parseProviderInfoXml(component, ri);
1539 if (parsed != null) {
1540 keep.add(ai.name);
1541 // Use the new AppWidgetProviderInfo.
1542 p.info = parsed.info;
1543 // If it's enabled
1544 final int M = p.instances.size();
1545 if (M > 0) {
1546 int[] appWidgetIds = getAppWidgetIds(p);
1547 // Reschedule for the new updatePeriodMillis (don't worry about handling
1548 // it specially if updatePeriodMillis didn't change because we just sent
1549 // an update, and the next one will be updatePeriodMillis from now).
1550 cancelBroadcasts(p);
1551 registerForBroadcastsLocked(p, appWidgetIds);
1552 // If it's currently showing, call back with the new
1553 // AppWidgetProviderInfo.
1554 for (int j = 0; j < M; j++) {
1555 AppWidgetId id = p.instances.get(j);
1556 id.views = null;
1557 if (id.host != null && id.host.callbacks != null) {
1558 try {
1559 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1560 } catch (RemoteException ex) {
1561 // It failed; remove the callback. No need to prune because
1562 // we know that this host is still referenced by this
1563 // instance.
1564 id.host.callbacks = null;
1565 }
1566 }
1567 }
1568 // Now that we've told the host, push out an update.
1569 sendUpdateIntentLocked(p, appWidgetIds);
1570 }
1571 }
1572 }
1573 }
1574 }
1575
1576 // prune the ones we don't want to keep
1577 N = mInstalledProviders.size();
1578 for (int i = N - 1; i >= 0; i--) {
1579 Provider p = mInstalledProviders.get(i);
1580 if (pkgName.equals(p.info.provider.getPackageName())
1581 && !keep.contains(p.info.provider.getClassName())) {
1582 removeProviderLocked(i, p);
1583 }
1584 }
1585 }
1586
1587 void removeProvidersForPackageLocked(String pkgName) {
1588 int N = mInstalledProviders.size();
1589 for (int i = N - 1; i >= 0; i--) {
1590 Provider p = mInstalledProviders.get(i);
1591 if (pkgName.equals(p.info.provider.getPackageName())) {
1592 removeProviderLocked(i, p);
1593 }
1594 }
1595
1596 // Delete the hosts for this package too
1597 //
1598 // By now, we have removed any AppWidgets that were in any hosts here,
1599 // so we don't need to worry about sending DISABLE broadcasts to them.
1600 N = mHosts.size();
1601 for (int i = N - 1; i >= 0; i--) {
1602 Host host = mHosts.get(i);
1603 if (pkgName.equals(host.packageName)) {
1604 deleteHostLocked(host);
1605 }
1606 }
1607 }
1608}