blob: 9c408c40826b1b22fd8be8ae25cbd8c64d4224da [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);
Amith Yamasani67cf7d32012-02-16 14:31:23 -0800466 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700467 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);
Amith Yamasani67cf7d32012-02-16 14:31:23 -0800474 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700475 }
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
Amith Yamasani742a6712011-05-04 14:49:28 -0700518 id.provider = p;
519 p.instances.add(id);
520 int instancesSize = p.instances.size();
521 if (instancesSize == 1) {
522 // tell the provider that it's ready
523 sendEnableIntentLocked(p);
524 }
525
526 // send an update now -- We need this update now, and just for this appWidgetId.
527 // It's less critical when the next one happens, so when we schedule the next one,
528 // we add updatePeriodMillis to its start time. That time will have some slop,
529 // but that's okay.
530 sendUpdateIntentLocked(p, new int[] { appWidgetId });
531
532 // schedule the future updates
533 registerForBroadcastsLocked(p, getAppWidgetIds(p));
534 saveStateLocked();
535 }
536 } finally {
537 Binder.restoreCallingIdentity(ident);
538 }
539 }
540
541 // Binds to a specific RemoteViewsService
542 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
543 synchronized (mAppWidgetIds) {
544 ensureStateLoadedLocked();
545 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
546 if (id == null) {
547 throw new IllegalArgumentException("bad appWidgetId");
548 }
549 final ComponentName componentName = intent.getComponent();
550 try {
551 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
552 PackageManager.GET_PERMISSIONS);
553 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
554 throw new SecurityException("Selected service does not require "
555 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
556 }
557 } catch (PackageManager.NameNotFoundException e) {
558 throw new IllegalArgumentException("Unknown component " + componentName);
559 }
560
561 // If there is already a connection made for this service intent, then disconnect from
562 // that first. (This does not allow multiple connections to the same service under
563 // the same key)
564 ServiceConnectionProxy conn = null;
565 FilterComparison fc = new FilterComparison(intent);
566 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
567 if (mBoundRemoteViewsServices.containsKey(key)) {
568 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
569 conn.disconnect();
570 mContext.unbindService(conn);
571 mBoundRemoteViewsServices.remove(key);
572 }
573
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800574 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700575 // Bind to the RemoteViewsService (which will trigger a callback to the
576 // RemoteViewsAdapter.onServiceConnected())
577 final long token = Binder.clearCallingIdentity();
578 try {
579 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800580 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700581 mBoundRemoteViewsServices.put(key, conn);
582 } finally {
583 Binder.restoreCallingIdentity(token);
584 }
585
586 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
587 // when we can call back to the RemoteViewsService later to destroy associated
588 // factories.
589 incrementAppWidgetServiceRefCount(appWidgetId, fc);
590 }
591 }
592
593 // Unbinds from a specific RemoteViewsService
594 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
595 synchronized (mAppWidgetIds) {
596 ensureStateLoadedLocked();
597 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
598 // RemoteViewsAdapter)
599 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
600 intent));
601 if (mBoundRemoteViewsServices.containsKey(key)) {
602 // We don't need to use the appWidgetId until after we are sure there is something
603 // to unbind. Note that this may mask certain issues with apps calling unbind()
604 // more than necessary.
605 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
606 if (id == null) {
607 throw new IllegalArgumentException("bad appWidgetId");
608 }
609
610 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
611 .get(key);
612 conn.disconnect();
613 mContext.unbindService(conn);
614 mBoundRemoteViewsServices.remove(key);
615 } else {
616 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
617 }
618 }
619 }
620
621 // Unbinds from a RemoteViewsService when we delete an app widget
622 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
623 int appWidgetId = id.appWidgetId;
624 // Unbind all connections to Services bound to this AppWidgetId
625 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
626 .iterator();
627 while (it.hasNext()) {
628 final Pair<Integer, Intent.FilterComparison> key = it.next();
629 if (key.first.intValue() == appWidgetId) {
630 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
631 .get(key);
632 conn.disconnect();
633 mContext.unbindService(conn);
634 it.remove();
635 }
636 }
637
638 // Check if we need to destroy any services (if no other app widgets are
639 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800640 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700641 }
642
643 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800644 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700645 final ServiceConnection conn = new ServiceConnection() {
646 @Override
647 public void onServiceConnected(ComponentName name, IBinder service) {
648 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
649 try {
650 cb.onDestroy(intent);
651 } catch (RemoteException e) {
652 e.printStackTrace();
653 } catch (RuntimeException e) {
654 e.printStackTrace();
655 }
656 mContext.unbindService(this);
657 }
658
659 @Override
660 public void onServiceDisconnected(android.content.ComponentName name) {
661 // Do nothing
662 }
663 };
664
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800665 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700666 // Bind to the service and remove the static intent->factory mapping in the
667 // RemoteViewsService.
668 final long token = Binder.clearCallingIdentity();
669 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800670 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700671 } 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.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800690 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700691 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
692 while (it.hasNext()) {
693 final FilterComparison key = it.next();
694 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800695 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700696 // 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()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800699 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700700 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
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800891 int userId = UserId.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700892 // Bind to the service and call onDataSetChanged()
893 final long token = Binder.clearCallingIdentity();
894 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800895 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700896 } finally {
897 Binder.restoreCallingIdentity(token);
898 }
899 }
900 }
901 }
902 }
903 }
904
905 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
906 List<RemoteViews> updatedViews) {
907 int callingUid = enforceCallingUid(packageName);
908 synchronized (mAppWidgetIds) {
909 ensureStateLoadedLocked();
910 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
911 host.callbacks = callbacks;
912
913 updatedViews.clear();
914
915 ArrayList<AppWidgetId> instances = host.instances;
916 int N = instances.size();
917 int[] updatedIds = new int[N];
918 for (int i = 0; i < N; i++) {
919 AppWidgetId id = instances.get(i);
920 updatedIds[i] = id.appWidgetId;
921 updatedViews.add(id.views);
922 }
923 return updatedIds;
924 }
925 }
926
927 public void stopListening(int hostId) {
928 synchronized (mAppWidgetIds) {
929 ensureStateLoadedLocked();
930 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
931 if (host != null) {
932 host.callbacks = null;
933 pruneHostLocked(host);
934 }
935 }
936 }
937
938 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
939 if (id.host.uid == callingUid) {
940 // Apps hosting the AppWidget have access to it.
941 return true;
942 }
943 if (id.provider != null && id.provider.uid == callingUid) {
944 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
945 return true;
946 }
947 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
948 // Apps that can bind have access to all appWidgetIds.
949 return true;
950 }
951 // Nobody else can access it.
952 return false;
953 }
954
955 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
956 int callingUid = Binder.getCallingUid();
957 final int N = mAppWidgetIds.size();
958 for (int i = 0; i < N; i++) {
959 AppWidgetId id = mAppWidgetIds.get(i);
960 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
961 return id;
962 }
963 }
964 return null;
965 }
966
967 Provider lookupProviderLocked(ComponentName provider) {
968 final int N = mInstalledProviders.size();
969 for (int i = 0; i < N; i++) {
970 Provider p = mInstalledProviders.get(i);
971 if (p.info.provider.equals(provider)) {
972 return p;
973 }
974 }
975 return null;
976 }
977
978 Host lookupHostLocked(int uid, int hostId) {
979 final int N = mHosts.size();
980 for (int i = 0; i < N; i++) {
981 Host h = mHosts.get(i);
982 if (h.uid == uid && h.hostId == hostId) {
983 return h;
984 }
985 }
986 return null;
987 }
988
989 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
990 final int N = mHosts.size();
991 for (int i = 0; i < N; i++) {
992 Host h = mHosts.get(i);
993 if (h.hostId == hostId && h.packageName.equals(packageName)) {
994 return h;
995 }
996 }
997 Host host = new Host();
998 host.packageName = packageName;
999 host.uid = uid;
1000 host.hostId = hostId;
1001 mHosts.add(host);
1002 return host;
1003 }
1004
1005 void pruneHostLocked(Host host) {
1006 if (host.instances.size() == 0 && host.callbacks == null) {
1007 mHosts.remove(host);
1008 }
1009 }
1010
1011 void loadAppWidgetList() {
1012 PackageManager pm = mPackageManager;
1013
1014 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1015 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
1016 PackageManager.GET_META_DATA);
1017
1018 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1019 for (int i = 0; i < N; i++) {
1020 ResolveInfo ri = broadcastReceivers.get(i);
1021 addProviderLocked(ri);
1022 }
1023 }
1024
1025 boolean addProviderLocked(ResolveInfo ri) {
1026 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1027 return false;
1028 }
1029 if (!ri.activityInfo.isEnabled()) {
1030 return false;
1031 }
1032 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1033 ri.activityInfo.name), ri);
1034 if (p != null) {
1035 mInstalledProviders.add(p);
1036 return true;
1037 } else {
1038 return false;
1039 }
1040 }
1041
1042 void removeProviderLocked(int index, Provider p) {
1043 int N = p.instances.size();
1044 for (int i = 0; i < N; i++) {
1045 AppWidgetId id = p.instances.get(i);
1046 // Call back with empty RemoteViews
1047 updateAppWidgetInstanceLocked(id, null);
1048 // Stop telling the host about updates for this from now on
1049 cancelBroadcasts(p);
1050 // clear out references to this appWidgetId
1051 id.host.instances.remove(id);
1052 mAppWidgetIds.remove(id);
1053 id.provider = null;
1054 pruneHostLocked(id.host);
1055 id.host = null;
1056 }
1057 p.instances.clear();
1058 mInstalledProviders.remove(index);
1059 mDeletedProviders.add(p);
1060 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1061 cancelBroadcasts(p);
1062 }
1063
1064 void sendEnableIntentLocked(Provider p) {
1065 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1066 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001067 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001068 }
1069
1070 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1071 if (appWidgetIds != null && appWidgetIds.length > 0) {
1072 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1073 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1074 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001075 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001076 }
1077 }
1078
1079 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1080 if (p.info.updatePeriodMillis > 0) {
1081 // if this is the first instance, set the alarm. otherwise,
1082 // rely on the fact that we've already set it and that
1083 // PendingIntent.getBroadcast will update the extras.
1084 boolean alreadyRegistered = p.broadcast != null;
1085 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1086 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1087 intent.setComponent(p.info.provider);
1088 long token = Binder.clearCallingIdentity();
1089 try {
1090 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1091 PendingIntent.FLAG_UPDATE_CURRENT);
1092 } finally {
1093 Binder.restoreCallingIdentity(token);
1094 }
1095 if (!alreadyRegistered) {
1096 long period = p.info.updatePeriodMillis;
1097 if (period < MIN_UPDATE_PERIOD) {
1098 period = MIN_UPDATE_PERIOD;
1099 }
1100 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1101 .elapsedRealtime()
1102 + period, period, p.broadcast);
1103 }
1104 }
1105 }
1106
1107 static int[] getAppWidgetIds(Provider p) {
1108 int instancesSize = p.instances.size();
1109 int appWidgetIds[] = new int[instancesSize];
1110 for (int i = 0; i < instancesSize; i++) {
1111 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1112 }
1113 return appWidgetIds;
1114 }
1115
1116 public int[] getAppWidgetIds(ComponentName provider) {
1117 synchronized (mAppWidgetIds) {
1118 ensureStateLoadedLocked();
1119 Provider p = lookupProviderLocked(provider);
1120 if (p != null && Binder.getCallingUid() == p.uid) {
1121 return getAppWidgetIds(p);
1122 } else {
1123 return new int[0];
1124 }
1125 }
1126 }
1127
1128 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1129 Provider p = null;
1130
1131 ActivityInfo activityInfo = ri.activityInfo;
1132 XmlResourceParser parser = null;
1133 try {
1134 parser = activityInfo.loadXmlMetaData(mPackageManager,
1135 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1136 if (parser == null) {
1137 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1138 + " meta-data for " + "AppWidget provider '" + component + '\'');
1139 return null;
1140 }
1141
1142 AttributeSet attrs = Xml.asAttributeSet(parser);
1143
1144 int type;
1145 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1146 && type != XmlPullParser.START_TAG) {
1147 // drain whitespace, comments, etc.
1148 }
1149
1150 String nodeName = parser.getName();
1151 if (!"appwidget-provider".equals(nodeName)) {
1152 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1153 + " AppWidget provider '" + component + '\'');
1154 return null;
1155 }
1156
1157 p = new Provider();
1158 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1159 info.provider = component;
1160 p.uid = activityInfo.applicationInfo.uid;
1161
1162 Resources res = mPackageManager
1163 .getResourcesForApplication(activityInfo.applicationInfo);
1164
1165 TypedArray sa = res.obtainAttributes(attrs,
1166 com.android.internal.R.styleable.AppWidgetProviderInfo);
1167
1168 // These dimensions has to be resolved in the application's context.
1169 // We simply send back the raw complex data, which will be
1170 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1171 TypedValue value = sa
1172 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1173 info.minWidth = value != null ? value.data : 0;
1174 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1175 info.minHeight = value != null ? value.data : 0;
1176 value = sa.peekValue(
1177 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1178 info.minResizeWidth = value != null ? value.data : info.minWidth;
1179 value = sa.peekValue(
1180 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1181 info.minResizeHeight = value != null ? value.data : info.minHeight;
1182 info.updatePeriodMillis = sa.getInt(
1183 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1184 info.initialLayout = sa.getResourceId(
1185 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1186 String className = sa
1187 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1188 if (className != null) {
1189 info.configure = new ComponentName(component.getPackageName(), className);
1190 }
1191 info.label = activityInfo.loadLabel(mPackageManager).toString();
1192 info.icon = ri.getIconResource();
1193 info.previewImage = sa.getResourceId(
1194 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1195 info.autoAdvanceViewId = sa.getResourceId(
1196 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1197 info.resizeMode = sa.getInt(
1198 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1199 AppWidgetProviderInfo.RESIZE_NONE);
1200
1201 sa.recycle();
1202 } catch (Exception e) {
1203 // Ok to catch Exception here, because anything going wrong because
1204 // of what a client process passes to us should not be fatal for the
1205 // system process.
1206 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1207 return null;
1208 } finally {
1209 if (parser != null)
1210 parser.close();
1211 }
1212 return p;
1213 }
1214
1215 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1216 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1217 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1218 throw new PackageManager.NameNotFoundException();
1219 }
1220 return pkgInfo.applicationInfo.uid;
1221 }
1222
1223 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1224 int callingUid = Binder.getCallingUid();
1225 int packageUid;
1226 try {
1227 packageUid = getUidForPackage(packageName);
1228 } catch (PackageManager.NameNotFoundException ex) {
1229 throw new IllegalArgumentException("packageName and uid don't match packageName="
1230 + packageName);
1231 }
1232 if (!UserId.isSameApp(callingUid, packageUid)) {
1233 throw new IllegalArgumentException("packageName and uid don't match packageName="
1234 + packageName);
1235 }
1236 return callingUid;
1237 }
1238
1239 void sendInitialBroadcasts() {
1240 synchronized (mAppWidgetIds) {
1241 ensureStateLoadedLocked();
1242 final int N = mInstalledProviders.size();
1243 for (int i = 0; i < N; i++) {
1244 Provider p = mInstalledProviders.get(i);
1245 if (p.instances.size() > 0) {
1246 sendEnableIntentLocked(p);
1247 int[] appWidgetIds = getAppWidgetIds(p);
1248 sendUpdateIntentLocked(p, appWidgetIds);
1249 registerForBroadcastsLocked(p, appWidgetIds);
1250 }
1251 }
1252 }
1253 }
1254
1255 // only call from initialization -- it assumes that the data structures are all empty
1256 void loadStateLocked() {
1257 AtomicFile file = savedStateFile();
1258 try {
1259 FileInputStream stream = file.openRead();
1260 readStateFromFileLocked(stream);
1261
1262 if (stream != null) {
1263 try {
1264 stream.close();
1265 } catch (IOException e) {
1266 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1267 }
1268 }
1269 } catch (FileNotFoundException e) {
1270 Slog.w(TAG, "Failed to read state: " + e);
1271 }
1272 }
1273
1274 void saveStateLocked() {
1275 AtomicFile file = savedStateFile();
1276 FileOutputStream stream;
1277 try {
1278 stream = file.startWrite();
1279 if (writeStateToFileLocked(stream)) {
1280 file.finishWrite(stream);
1281 } else {
1282 file.failWrite(stream);
1283 Slog.w(TAG, "Failed to save state, restoring backup.");
1284 }
1285 } catch (IOException e) {
1286 Slog.w(TAG, "Failed open state file for write: " + e);
1287 }
1288 }
1289
1290 boolean writeStateToFileLocked(FileOutputStream stream) {
1291 int N;
1292
1293 try {
1294 XmlSerializer out = new FastXmlSerializer();
1295 out.setOutput(stream, "utf-8");
1296 out.startDocument(null, true);
1297 out.startTag(null, "gs");
1298
1299 int providerIndex = 0;
1300 N = mInstalledProviders.size();
1301 for (int i = 0; i < N; i++) {
1302 Provider p = mInstalledProviders.get(i);
1303 if (p.instances.size() > 0) {
1304 out.startTag(null, "p");
1305 out.attribute(null, "pkg", p.info.provider.getPackageName());
1306 out.attribute(null, "cl", p.info.provider.getClassName());
1307 out.endTag(null, "p");
1308 p.tag = providerIndex;
1309 providerIndex++;
1310 }
1311 }
1312
1313 N = mHosts.size();
1314 for (int i = 0; i < N; i++) {
1315 Host host = mHosts.get(i);
1316 out.startTag(null, "h");
1317 out.attribute(null, "pkg", host.packageName);
1318 out.attribute(null, "id", Integer.toHexString(host.hostId));
1319 out.endTag(null, "h");
1320 host.tag = i;
1321 }
1322
1323 N = mAppWidgetIds.size();
1324 for (int i = 0; i < N; i++) {
1325 AppWidgetId id = mAppWidgetIds.get(i);
1326 out.startTag(null, "g");
1327 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1328 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1329 if (id.provider != null) {
1330 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1331 }
1332 out.endTag(null, "g");
1333 }
1334
1335 out.endTag(null, "gs");
1336
1337 out.endDocument();
1338 return true;
1339 } catch (IOException e) {
1340 Slog.w(TAG, "Failed to write state: " + e);
1341 return false;
1342 }
1343 }
1344
1345 void readStateFromFileLocked(FileInputStream stream) {
1346 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001347 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() {
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001478 File dir = new File("/data/system/users/" + mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001479 File settingsFile = new File(dir, SETTINGS_FILENAME);
1480 if (!dir.exists()) {
1481 dir.mkdirs();
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001482 if (mUserId == 0) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001483 // Migrate old data
1484 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1485 // Method doesn't throw an exception on failure. Ignore any errors
1486 // in moving the file (like non-existence)
1487 oldFile.renameTo(settingsFile);
1488 }
1489 }
1490 return new AtomicFile(settingsFile);
1491 }
1492
1493 void addProvidersForPackageLocked(String pkgName) {
1494 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1495 intent.setPackage(pkgName);
1496 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1497 PackageManager.GET_META_DATA);
1498
1499 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1500 for (int i = 0; i < N; i++) {
1501 ResolveInfo ri = broadcastReceivers.get(i);
1502 ActivityInfo ai = ri.activityInfo;
1503 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1504 continue;
1505 }
1506 if (pkgName.equals(ai.packageName)) {
1507 addProviderLocked(ri);
1508 }
1509 }
1510 }
1511
1512 void updateProvidersForPackageLocked(String pkgName) {
1513 HashSet<String> keep = new HashSet<String>();
1514 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1515 intent.setPackage(pkgName);
1516 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1517 PackageManager.GET_META_DATA);
1518
1519 // add the missing ones and collect which ones to keep
1520 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1521 for (int i = 0; i < N; i++) {
1522 ResolveInfo ri = broadcastReceivers.get(i);
1523 ActivityInfo ai = ri.activityInfo;
1524 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1525 continue;
1526 }
1527 if (pkgName.equals(ai.packageName)) {
1528 ComponentName component = new ComponentName(ai.packageName, ai.name);
1529 Provider p = lookupProviderLocked(component);
1530 if (p == null) {
1531 if (addProviderLocked(ri)) {
1532 keep.add(ai.name);
1533 }
1534 } else {
1535 Provider parsed = parseProviderInfoXml(component, ri);
1536 if (parsed != null) {
1537 keep.add(ai.name);
1538 // Use the new AppWidgetProviderInfo.
1539 p.info = parsed.info;
1540 // If it's enabled
1541 final int M = p.instances.size();
1542 if (M > 0) {
1543 int[] appWidgetIds = getAppWidgetIds(p);
1544 // Reschedule for the new updatePeriodMillis (don't worry about handling
1545 // it specially if updatePeriodMillis didn't change because we just sent
1546 // an update, and the next one will be updatePeriodMillis from now).
1547 cancelBroadcasts(p);
1548 registerForBroadcastsLocked(p, appWidgetIds);
1549 // If it's currently showing, call back with the new
1550 // AppWidgetProviderInfo.
1551 for (int j = 0; j < M; j++) {
1552 AppWidgetId id = p.instances.get(j);
1553 id.views = null;
1554 if (id.host != null && id.host.callbacks != null) {
1555 try {
1556 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1557 } catch (RemoteException ex) {
1558 // It failed; remove the callback. No need to prune because
1559 // we know that this host is still referenced by this
1560 // instance.
1561 id.host.callbacks = null;
1562 }
1563 }
1564 }
1565 // Now that we've told the host, push out an update.
1566 sendUpdateIntentLocked(p, appWidgetIds);
1567 }
1568 }
1569 }
1570 }
1571 }
1572
1573 // prune the ones we don't want to keep
1574 N = mInstalledProviders.size();
1575 for (int i = N - 1; i >= 0; i--) {
1576 Provider p = mInstalledProviders.get(i);
1577 if (pkgName.equals(p.info.provider.getPackageName())
1578 && !keep.contains(p.info.provider.getClassName())) {
1579 removeProviderLocked(i, p);
1580 }
1581 }
1582 }
1583
1584 void removeProvidersForPackageLocked(String pkgName) {
1585 int N = mInstalledProviders.size();
1586 for (int i = N - 1; i >= 0; i--) {
1587 Provider p = mInstalledProviders.get(i);
1588 if (pkgName.equals(p.info.provider.getPackageName())) {
1589 removeProviderLocked(i, p);
1590 }
1591 }
1592
1593 // Delete the hosts for this package too
1594 //
1595 // By now, we have removed any AppWidgets that were in any hosts here,
1596 // so we don't need to worry about sending DISABLE broadcasts to them.
1597 N = mHosts.size();
1598 for (int i = N - 1; i >= 0; i--) {
1599 Host host = mHosts.get(i);
1600 if (pkgName.equals(host.packageName)) {
1601 deleteHostLocked(host);
1602 }
1603 }
1604 }
1605}