blob: 182a8849f4ce88f9113111da232ff4592b4ba8ce [file] [log] [blame]
Amith Yamasani742a6712011-05-04 14:49:28 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.AlarmManager;
Amith Yamasani483f3b02012-03-13 16:08:00 -070020import android.app.AppGlobals;
Amith Yamasani742a6712011-05-04 14:49:28 -070021import android.app.PendingIntent;
22import android.appwidget.AppWidgetManager;
23import android.appwidget.AppWidgetProviderInfo;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.ServiceConnection;
28import android.content.Intent.FilterComparison;
29import android.content.pm.ActivityInfo;
30import android.content.pm.ApplicationInfo;
Amith Yamasani483f3b02012-03-13 16:08:00 -070031import android.content.pm.IPackageManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070032import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.ResolveInfo;
35import android.content.pm.ServiceInfo;
36import android.content.res.Resources;
37import android.content.res.TypedArray;
38import android.content.res.XmlResourceParser;
39import android.net.Uri;
40import android.os.Binder;
41import android.os.Bundle;
42import android.os.IBinder;
43import android.os.RemoteException;
44import android.os.SystemClock;
45import android.os.UserId;
46import android.util.AttributeSet;
47import android.util.Log;
48import android.util.Pair;
49import android.util.Slog;
50import android.util.TypedValue;
51import android.util.Xml;
52import android.widget.RemoteViews;
53
54import com.android.internal.appwidget.IAppWidgetHost;
55import com.android.internal.os.AtomicFile;
56import com.android.internal.util.FastXmlSerializer;
57import com.android.internal.widget.IRemoteViewsAdapterConnection;
58import com.android.internal.widget.IRemoteViewsFactory;
59import com.android.server.am.ActivityManagerService;
60
61import org.xmlpull.v1.XmlPullParser;
62import org.xmlpull.v1.XmlPullParserException;
63import org.xmlpull.v1.XmlSerializer;
64
65import java.io.File;
66import java.io.FileDescriptor;
67import java.io.FileInputStream;
68import java.io.FileNotFoundException;
69import java.io.FileOutputStream;
70import java.io.IOException;
71import java.io.PrintWriter;
72import java.util.ArrayList;
73import java.util.HashMap;
74import java.util.HashSet;
75import java.util.Iterator;
76import java.util.List;
77import java.util.Locale;
78import java.util.Set;
79
80class AppWidgetServiceImpl {
81
82 private static final String TAG = "AppWidgetServiceImpl";
83 private static final String SETTINGS_FILENAME = "appwidgets.xml";
84 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
85
86 /*
87 * When identifying a Host or Provider based on the calling process, use the uid field. When
88 * identifying a Host or Provider based on a package manager broadcast, use the package given.
89 */
90
91 static class Provider {
92 int uid;
93 AppWidgetProviderInfo info;
94 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
95 PendingIntent broadcast;
96 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
97
98 int tag; // for use while saving state (the index)
99 }
100
101 static class Host {
102 int uid;
103 int hostId;
104 String packageName;
105 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
106 IAppWidgetHost callbacks;
107 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
108
109 int tag; // for use while saving state (the index)
110 }
111
112 static class AppWidgetId {
113 int appWidgetId;
114 Provider provider;
115 RemoteViews views;
116 Host host;
117 }
118
119 /**
120 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
121 * needs to be a static inner class since a reference to the ServiceConnection is held globally
122 * and may lead us to leak AppWidgetService instances (if there were more than one).
123 */
124 static class ServiceConnectionProxy implements ServiceConnection {
125 private final IBinder mConnectionCb;
126
127 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
128 mConnectionCb = connectionCb;
129 }
130
131 public void onServiceConnected(ComponentName name, IBinder service) {
132 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
133 .asInterface(mConnectionCb);
134 try {
135 cb.onServiceConnected(service);
136 } catch (Exception e) {
137 e.printStackTrace();
138 }
139 }
140
141 public void onServiceDisconnected(ComponentName name) {
142 disconnect();
143 }
144
145 public void disconnect() {
146 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
147 .asInterface(mConnectionCb);
148 try {
149 cb.onServiceDisconnected();
150 } catch (Exception e) {
151 e.printStackTrace();
152 }
153 }
154 }
155
156 // Manages active connections to RemoteViewsServices
157 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
158 // Manages persistent references to RemoteViewsServices from different App Widgets
159 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
160
161 Context mContext;
162 Locale mLocale;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700163 IPackageManager mPm;
Amith Yamasani742a6712011-05-04 14:49:28 -0700164 AlarmManager mAlarmManager;
165 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
166 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
167 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
168 ArrayList<Host> mHosts = new ArrayList<Host>();
169 boolean mSafeMode;
170 int mUserId;
171 boolean mStateLoaded;
172
173 // These are for debugging only -- widgets are going missing in some rare instances
174 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
175 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
176
177 AppWidgetServiceImpl(Context context, int userId) {
178 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700179 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700180 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
181 mUserId = userId;
182 }
183
184 public void systemReady(boolean safeMode) {
185 mSafeMode = safeMode;
186
187 synchronized (mAppWidgetIds) {
188 ensureStateLoadedLocked();
189 }
190 }
191
192 void onConfigurationChanged() {
193 Locale revised = Locale.getDefault();
194 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
195 mLocale = revised;
196
197 synchronized (mAppWidgetIds) {
198 ensureStateLoadedLocked();
199 int N = mInstalledProviders.size();
200 for (int i = N - 1; i >= 0; i--) {
201 Provider p = mInstalledProviders.get(i);
202 String pkgName = p.info.provider.getPackageName();
203 updateProvidersForPackageLocked(pkgName);
204 }
205 saveStateLocked();
206 }
207 }
208 }
209
210 void onBroadcastReceived(Intent intent) {
211 final String action = intent.getAction();
212 boolean added = false;
213 boolean changed = false;
214 String pkgList[] = null;
215 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
216 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
217 added = true;
218 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
219 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
220 added = false;
221 } else {
222 Uri uri = intent.getData();
223 if (uri == null) {
224 return;
225 }
226 String pkgName = uri.getSchemeSpecificPart();
227 if (pkgName == null) {
228 return;
229 }
230 pkgList = new String[] { pkgName };
231 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
232 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
233 }
234 if (pkgList == null || pkgList.length == 0) {
235 return;
236 }
237 if (added || changed) {
238 synchronized (mAppWidgetIds) {
239 ensureStateLoadedLocked();
240 Bundle extras = intent.getExtras();
241 if (changed
242 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
243 for (String pkgName : pkgList) {
244 // The package was just upgraded
245 updateProvidersForPackageLocked(pkgName);
246 }
247 } else {
248 // The package was just added
249 for (String pkgName : pkgList) {
250 addProvidersForPackageLocked(pkgName);
251 }
252 }
253 saveStateLocked();
254 }
255 } else {
256 Bundle extras = intent.getExtras();
257 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
258 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
259 } else {
260 synchronized (mAppWidgetIds) {
261 ensureStateLoadedLocked();
262 for (String pkgName : pkgList) {
263 removeProvidersForPackageLocked(pkgName);
264 saveStateLocked();
265 }
266 }
267 }
268 }
269 }
270
271 private void dumpProvider(Provider p, int index, PrintWriter pw) {
272 AppWidgetProviderInfo info = p.info;
273 pw.print(" ["); pw.print(index); pw.print("] provider ");
274 pw.print(info.provider.flattenToShortString());
275 pw.println(':');
276 pw.print(" min=("); pw.print(info.minWidth);
277 pw.print("x"); pw.print(info.minHeight);
278 pw.print(") minResize=("); pw.print(info.minResizeWidth);
279 pw.print("x"); pw.print(info.minResizeHeight);
280 pw.print(") updatePeriodMillis=");
281 pw.print(info.updatePeriodMillis);
282 pw.print(" resizeMode=");
283 pw.print(info.resizeMode);
284 pw.print(" autoAdvanceViewId=");
285 pw.print(info.autoAdvanceViewId);
286 pw.print(" initialLayout=#");
287 pw.print(Integer.toHexString(info.initialLayout));
288 pw.print(" zombie="); pw.println(p.zombie);
289 }
290
291 private void dumpHost(Host host, int index, PrintWriter pw) {
292 pw.print(" ["); pw.print(index); pw.print("] hostId=");
293 pw.print(host.hostId); pw.print(' ');
294 pw.print(host.packageName); pw.print('/');
295 pw.print(host.uid); pw.println(':');
296 pw.print(" callbacks="); pw.println(host.callbacks);
297 pw.print(" instances.size="); pw.print(host.instances.size());
298 pw.print(" zombie="); pw.println(host.zombie);
299 }
300
301 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
302 pw.print(" ["); pw.print(index); pw.print("] id=");
303 pw.println(id.appWidgetId);
304 pw.print(" hostId=");
305 pw.print(id.host.hostId); pw.print(' ');
306 pw.print(id.host.packageName); pw.print('/');
307 pw.println(id.host.uid);
308 if (id.provider != null) {
309 pw.print(" provider=");
310 pw.println(id.provider.info.provider.flattenToShortString());
311 }
312 if (id.host != null) {
313 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
314 }
315 if (id.views != null) {
316 pw.print(" views="); pw.println(id.views);
317 }
318 }
319
320 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
321 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
322 != PackageManager.PERMISSION_GRANTED) {
323 pw.println("Permission Denial: can't dump from from pid="
324 + Binder.getCallingPid()
325 + ", uid=" + Binder.getCallingUid());
326 return;
327 }
328
329 synchronized (mAppWidgetIds) {
330 int N = mInstalledProviders.size();
331 pw.println("Providers:");
332 for (int i=0; i<N; i++) {
333 dumpProvider(mInstalledProviders.get(i), i, pw);
334 }
335
336 N = mAppWidgetIds.size();
337 pw.println(" ");
338 pw.println("AppWidgetIds:");
339 for (int i=0; i<N; i++) {
340 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
341 }
342
343 N = mHosts.size();
344 pw.println(" ");
345 pw.println("Hosts:");
346 for (int i=0; i<N; i++) {
347 dumpHost(mHosts.get(i), i, pw);
348 }
349
350 N = mDeletedProviders.size();
351 pw.println(" ");
352 pw.println("Deleted Providers:");
353 for (int i=0; i<N; i++) {
354 dumpProvider(mDeletedProviders.get(i), i, pw);
355 }
356
357 N = mDeletedHosts.size();
358 pw.println(" ");
359 pw.println("Deleted Hosts:");
360 for (int i=0; i<N; i++) {
361 dumpHost(mDeletedHosts.get(i), i, pw);
362 }
363 }
364 }
365
366 private void ensureStateLoadedLocked() {
367 if (!mStateLoaded) {
368 loadAppWidgetList();
369 loadStateLocked();
370 mStateLoaded = true;
371 }
372 }
373
374 public int allocateAppWidgetId(String packageName, int hostId) {
375 int callingUid = enforceCallingUid(packageName);
376 synchronized (mAppWidgetIds) {
377 ensureStateLoadedLocked();
378 int appWidgetId = mNextAppWidgetId++;
379
380 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
381
382 AppWidgetId id = new AppWidgetId();
383 id.appWidgetId = appWidgetId;
384 id.host = host;
385
386 host.instances.add(id);
387 mAppWidgetIds.add(id);
388
389 saveStateLocked();
390
391 return appWidgetId;
392 }
393 }
394
395 public void deleteAppWidgetId(int appWidgetId) {
396 synchronized (mAppWidgetIds) {
397 ensureStateLoadedLocked();
398 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
399 if (id != null) {
400 deleteAppWidgetLocked(id);
401 saveStateLocked();
402 }
403 }
404 }
405
406 public void deleteHost(int hostId) {
407 synchronized (mAppWidgetIds) {
408 ensureStateLoadedLocked();
409 int callingUid = Binder.getCallingUid();
410 Host host = lookupHostLocked(callingUid, hostId);
411 if (host != null) {
412 deleteHostLocked(host);
413 saveStateLocked();
414 }
415 }
416 }
417
418 public void deleteAllHosts() {
419 synchronized (mAppWidgetIds) {
420 ensureStateLoadedLocked();
421 int callingUid = Binder.getCallingUid();
422 final int N = mHosts.size();
423 boolean changed = false;
424 for (int i = N - 1; i >= 0; i--) {
425 Host host = mHosts.get(i);
426 if (host.uid == callingUid) {
427 deleteHostLocked(host);
428 changed = true;
429 }
430 }
431 if (changed) {
432 saveStateLocked();
433 }
434 }
435 }
436
437 void deleteHostLocked(Host host) {
438 final int N = host.instances.size();
439 for (int i = N - 1; i >= 0; i--) {
440 AppWidgetId id = host.instances.get(i);
441 deleteAppWidgetLocked(id);
442 }
443 host.instances.clear();
444 mHosts.remove(host);
445 mDeletedHosts.add(host);
446 // it's gone or going away, abruptly drop the callback connection
447 host.callbacks = null;
448 }
449
450 void deleteAppWidgetLocked(AppWidgetId id) {
451 // We first unbind all services that are bound to this id
452 unbindAppWidgetRemoteViewsServicesLocked(id);
453
454 Host host = id.host;
455 host.instances.remove(id);
456 pruneHostLocked(host);
457
458 mAppWidgetIds.remove(id);
459
460 Provider p = id.provider;
461 if (p != null) {
462 p.instances.remove(id);
463 if (!p.zombie) {
464 // send the broacast saying that this appWidgetId has been deleted
465 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
466 intent.setComponent(p.info.provider);
467 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Amith Yamasani67cf7d32012-02-16 14:31:23 -0800468 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700469 if (p.instances.size() == 0) {
470 // cancel the future updates
471 cancelBroadcasts(p);
472
473 // send the broacast saying that the provider is not in use any more
474 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
475 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -0800476 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700477 }
478 }
479 }
480 }
481
482 void cancelBroadcasts(Provider p) {
483 if (p.broadcast != null) {
484 mAlarmManager.cancel(p.broadcast);
485 long token = Binder.clearCallingIdentity();
486 try {
487 p.broadcast.cancel();
488 } finally {
489 Binder.restoreCallingIdentity(token);
490 }
491 p.broadcast = null;
492 }
493 }
494
495 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
496 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
497 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
498
499 final long ident = Binder.clearCallingIdentity();
500 try {
501 synchronized (mAppWidgetIds) {
502 ensureStateLoadedLocked();
503 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
504 if (id == null) {
505 throw new IllegalArgumentException("bad appWidgetId");
506 }
507 if (id.provider != null) {
508 throw new IllegalArgumentException("appWidgetId " + appWidgetId
509 + " already bound to " + id.provider.info.provider);
510 }
511 Provider p = lookupProviderLocked(provider);
512 if (p == null) {
513 throw new IllegalArgumentException("not a appwidget provider: " + provider);
514 }
515 if (p.zombie) {
516 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
517 + " safe mode: " + provider);
518 }
519
Amith Yamasani742a6712011-05-04 14:49:28 -0700520 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() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001014 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001015 try {
1016 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1017 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1018 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001019
Amith Yamasani483f3b02012-03-13 16:08:00 -07001020 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 } catch (RemoteException re) {
1026 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001027 }
1028 }
1029
1030 boolean addProviderLocked(ResolveInfo ri) {
1031 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1032 return false;
1033 }
1034 if (!ri.activityInfo.isEnabled()) {
1035 return false;
1036 }
1037 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1038 ri.activityInfo.name), ri);
1039 if (p != null) {
1040 mInstalledProviders.add(p);
1041 return true;
1042 } else {
1043 return false;
1044 }
1045 }
1046
1047 void removeProviderLocked(int index, Provider p) {
1048 int N = p.instances.size();
1049 for (int i = 0; i < N; i++) {
1050 AppWidgetId id = p.instances.get(i);
1051 // Call back with empty RemoteViews
1052 updateAppWidgetInstanceLocked(id, null);
1053 // Stop telling the host about updates for this from now on
1054 cancelBroadcasts(p);
1055 // clear out references to this appWidgetId
1056 id.host.instances.remove(id);
1057 mAppWidgetIds.remove(id);
1058 id.provider = null;
1059 pruneHostLocked(id.host);
1060 id.host = null;
1061 }
1062 p.instances.clear();
1063 mInstalledProviders.remove(index);
1064 mDeletedProviders.add(p);
1065 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1066 cancelBroadcasts(p);
1067 }
1068
1069 void sendEnableIntentLocked(Provider p) {
1070 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1071 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001072 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001073 }
1074
1075 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1076 if (appWidgetIds != null && appWidgetIds.length > 0) {
1077 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1078 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1079 intent.setComponent(p.info.provider);
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001080 mContext.sendBroadcast(intent, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001081 }
1082 }
1083
1084 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1085 if (p.info.updatePeriodMillis > 0) {
1086 // if this is the first instance, set the alarm. otherwise,
1087 // rely on the fact that we've already set it and that
1088 // PendingIntent.getBroadcast will update the extras.
1089 boolean alreadyRegistered = p.broadcast != null;
1090 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1091 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1092 intent.setComponent(p.info.provider);
1093 long token = Binder.clearCallingIdentity();
1094 try {
1095 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1096 PendingIntent.FLAG_UPDATE_CURRENT);
1097 } finally {
1098 Binder.restoreCallingIdentity(token);
1099 }
1100 if (!alreadyRegistered) {
1101 long period = p.info.updatePeriodMillis;
1102 if (period < MIN_UPDATE_PERIOD) {
1103 period = MIN_UPDATE_PERIOD;
1104 }
1105 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1106 .elapsedRealtime()
1107 + period, period, p.broadcast);
1108 }
1109 }
1110 }
1111
1112 static int[] getAppWidgetIds(Provider p) {
1113 int instancesSize = p.instances.size();
1114 int appWidgetIds[] = new int[instancesSize];
1115 for (int i = 0; i < instancesSize; i++) {
1116 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1117 }
1118 return appWidgetIds;
1119 }
1120
1121 public int[] getAppWidgetIds(ComponentName provider) {
1122 synchronized (mAppWidgetIds) {
1123 ensureStateLoadedLocked();
1124 Provider p = lookupProviderLocked(provider);
1125 if (p != null && Binder.getCallingUid() == p.uid) {
1126 return getAppWidgetIds(p);
1127 } else {
1128 return new int[0];
1129 }
1130 }
1131 }
1132
1133 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1134 Provider p = null;
1135
1136 ActivityInfo activityInfo = ri.activityInfo;
1137 XmlResourceParser parser = null;
1138 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001139 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001140 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1141 if (parser == null) {
1142 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1143 + " meta-data for " + "AppWidget provider '" + component + '\'');
1144 return null;
1145 }
1146
1147 AttributeSet attrs = Xml.asAttributeSet(parser);
1148
1149 int type;
1150 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1151 && type != XmlPullParser.START_TAG) {
1152 // drain whitespace, comments, etc.
1153 }
1154
1155 String nodeName = parser.getName();
1156 if (!"appwidget-provider".equals(nodeName)) {
1157 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1158 + " AppWidget provider '" + component + '\'');
1159 return null;
1160 }
1161
1162 p = new Provider();
1163 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1164 info.provider = component;
1165 p.uid = activityInfo.applicationInfo.uid;
1166
Amith Yamasani483f3b02012-03-13 16:08:00 -07001167 Resources res = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001168 .getResourcesForApplication(activityInfo.applicationInfo);
1169
1170 TypedArray sa = res.obtainAttributes(attrs,
1171 com.android.internal.R.styleable.AppWidgetProviderInfo);
1172
1173 // These dimensions has to be resolved in the application's context.
1174 // We simply send back the raw complex data, which will be
1175 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1176 TypedValue value = sa
1177 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1178 info.minWidth = value != null ? value.data : 0;
1179 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1180 info.minHeight = value != null ? value.data : 0;
1181 value = sa.peekValue(
1182 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1183 info.minResizeWidth = value != null ? value.data : info.minWidth;
1184 value = sa.peekValue(
1185 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1186 info.minResizeHeight = value != null ? value.data : info.minHeight;
1187 info.updatePeriodMillis = sa.getInt(
1188 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1189 info.initialLayout = sa.getResourceId(
1190 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1191 String className = sa
1192 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1193 if (className != null) {
1194 info.configure = new ComponentName(component.getPackageName(), className);
1195 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001196 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001197 info.icon = ri.getIconResource();
1198 info.previewImage = sa.getResourceId(
1199 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1200 info.autoAdvanceViewId = sa.getResourceId(
1201 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1202 info.resizeMode = sa.getInt(
1203 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1204 AppWidgetProviderInfo.RESIZE_NONE);
1205
1206 sa.recycle();
1207 } catch (Exception e) {
1208 // Ok to catch Exception here, because anything going wrong because
1209 // of what a client process passes to us should not be fatal for the
1210 // system process.
1211 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1212 return null;
1213 } finally {
1214 if (parser != null)
1215 parser.close();
1216 }
1217 return p;
1218 }
1219
1220 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001221 PackageInfo pkgInfo = null;
1222 try {
1223 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1224 } catch (RemoteException re) {
1225 // Shouldn't happen, local call
1226 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001227 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1228 throw new PackageManager.NameNotFoundException();
1229 }
1230 return pkgInfo.applicationInfo.uid;
1231 }
1232
1233 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1234 int callingUid = Binder.getCallingUid();
1235 int packageUid;
1236 try {
1237 packageUid = getUidForPackage(packageName);
1238 } catch (PackageManager.NameNotFoundException ex) {
1239 throw new IllegalArgumentException("packageName and uid don't match packageName="
1240 + packageName);
1241 }
1242 if (!UserId.isSameApp(callingUid, packageUid)) {
1243 throw new IllegalArgumentException("packageName and uid don't match packageName="
1244 + packageName);
1245 }
1246 return callingUid;
1247 }
1248
1249 void sendInitialBroadcasts() {
1250 synchronized (mAppWidgetIds) {
1251 ensureStateLoadedLocked();
1252 final int N = mInstalledProviders.size();
1253 for (int i = 0; i < N; i++) {
1254 Provider p = mInstalledProviders.get(i);
1255 if (p.instances.size() > 0) {
1256 sendEnableIntentLocked(p);
1257 int[] appWidgetIds = getAppWidgetIds(p);
1258 sendUpdateIntentLocked(p, appWidgetIds);
1259 registerForBroadcastsLocked(p, appWidgetIds);
1260 }
1261 }
1262 }
1263 }
1264
1265 // only call from initialization -- it assumes that the data structures are all empty
1266 void loadStateLocked() {
1267 AtomicFile file = savedStateFile();
1268 try {
1269 FileInputStream stream = file.openRead();
1270 readStateFromFileLocked(stream);
1271
1272 if (stream != null) {
1273 try {
1274 stream.close();
1275 } catch (IOException e) {
1276 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1277 }
1278 }
1279 } catch (FileNotFoundException e) {
1280 Slog.w(TAG, "Failed to read state: " + e);
1281 }
1282 }
1283
1284 void saveStateLocked() {
1285 AtomicFile file = savedStateFile();
1286 FileOutputStream stream;
1287 try {
1288 stream = file.startWrite();
1289 if (writeStateToFileLocked(stream)) {
1290 file.finishWrite(stream);
1291 } else {
1292 file.failWrite(stream);
1293 Slog.w(TAG, "Failed to save state, restoring backup.");
1294 }
1295 } catch (IOException e) {
1296 Slog.w(TAG, "Failed open state file for write: " + e);
1297 }
1298 }
1299
1300 boolean writeStateToFileLocked(FileOutputStream stream) {
1301 int N;
1302
1303 try {
1304 XmlSerializer out = new FastXmlSerializer();
1305 out.setOutput(stream, "utf-8");
1306 out.startDocument(null, true);
1307 out.startTag(null, "gs");
1308
1309 int providerIndex = 0;
1310 N = mInstalledProviders.size();
1311 for (int i = 0; i < N; i++) {
1312 Provider p = mInstalledProviders.get(i);
1313 if (p.instances.size() > 0) {
1314 out.startTag(null, "p");
1315 out.attribute(null, "pkg", p.info.provider.getPackageName());
1316 out.attribute(null, "cl", p.info.provider.getClassName());
1317 out.endTag(null, "p");
1318 p.tag = providerIndex;
1319 providerIndex++;
1320 }
1321 }
1322
1323 N = mHosts.size();
1324 for (int i = 0; i < N; i++) {
1325 Host host = mHosts.get(i);
1326 out.startTag(null, "h");
1327 out.attribute(null, "pkg", host.packageName);
1328 out.attribute(null, "id", Integer.toHexString(host.hostId));
1329 out.endTag(null, "h");
1330 host.tag = i;
1331 }
1332
1333 N = mAppWidgetIds.size();
1334 for (int i = 0; i < N; i++) {
1335 AppWidgetId id = mAppWidgetIds.get(i);
1336 out.startTag(null, "g");
1337 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1338 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1339 if (id.provider != null) {
1340 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1341 }
1342 out.endTag(null, "g");
1343 }
1344
1345 out.endTag(null, "gs");
1346
1347 out.endDocument();
1348 return true;
1349 } catch (IOException e) {
1350 Slog.w(TAG, "Failed to write state: " + e);
1351 return false;
1352 }
1353 }
1354
1355 void readStateFromFileLocked(FileInputStream stream) {
1356 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001357 try {
1358 XmlPullParser parser = Xml.newPullParser();
1359 parser.setInput(stream, null);
1360
1361 int type;
1362 int providerIndex = 0;
1363 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1364 do {
1365 type = parser.next();
1366 if (type == XmlPullParser.START_TAG) {
1367 String tag = parser.getName();
1368 if ("p".equals(tag)) {
1369 // TODO: do we need to check that this package has the same signature
1370 // as before?
1371 String pkg = parser.getAttributeValue(null, "pkg");
1372 String cl = parser.getAttributeValue(null, "cl");
1373
1374 final PackageManager packageManager = mContext.getPackageManager();
1375 try {
1376 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1377 } catch (PackageManager.NameNotFoundException e) {
1378 String[] pkgs = packageManager
1379 .currentToCanonicalPackageNames(new String[] { pkg });
1380 pkg = pkgs[0];
1381 }
1382
1383 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1384 if (p == null && mSafeMode) {
1385 // if we're in safe mode, make a temporary one
1386 p = new Provider();
1387 p.info = new AppWidgetProviderInfo();
1388 p.info.provider = new ComponentName(pkg, cl);
1389 p.zombie = true;
1390 mInstalledProviders.add(p);
1391 }
1392 if (p != null) {
1393 // if it wasn't uninstalled or something
1394 loadedProviders.put(providerIndex, p);
1395 }
1396 providerIndex++;
1397 } else if ("h".equals(tag)) {
1398 Host host = new Host();
1399
1400 // TODO: do we need to check that this package has the same signature
1401 // as before?
1402 host.packageName = parser.getAttributeValue(null, "pkg");
1403 try {
1404 host.uid = getUidForPackage(host.packageName);
1405 } catch (PackageManager.NameNotFoundException ex) {
1406 host.zombie = true;
1407 }
1408 if (!host.zombie || mSafeMode) {
1409 // In safe mode, we don't discard the hosts we don't recognize
1410 // so that they're not pruned from our list. Otherwise, we do.
1411 host.hostId = Integer
1412 .parseInt(parser.getAttributeValue(null, "id"), 16);
1413 mHosts.add(host);
1414 }
1415 } else if ("g".equals(tag)) {
1416 AppWidgetId id = new AppWidgetId();
1417 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1418 if (id.appWidgetId >= mNextAppWidgetId) {
1419 mNextAppWidgetId = id.appWidgetId + 1;
1420 }
1421
1422 String providerString = parser.getAttributeValue(null, "p");
1423 if (providerString != null) {
1424 // there's no provider if it hasn't been bound yet.
1425 // maybe we don't have to save this, but it brings the system
1426 // to the state it was in.
1427 int pIndex = Integer.parseInt(providerString, 16);
1428 id.provider = loadedProviders.get(pIndex);
1429 if (false) {
1430 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1431 + pIndex + " which is " + id.provider);
1432 }
1433 if (id.provider == null) {
1434 // This provider is gone. We just let the host figure out
1435 // that this happened when it fails to load it.
1436 continue;
1437 }
1438 }
1439
1440 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1441 id.host = mHosts.get(hIndex);
1442 if (id.host == null) {
1443 // This host is gone.
1444 continue;
1445 }
1446
1447 if (id.provider != null) {
1448 id.provider.instances.add(id);
1449 }
1450 id.host.instances.add(id);
1451 mAppWidgetIds.add(id);
1452 }
1453 }
1454 } while (type != XmlPullParser.END_DOCUMENT);
1455 success = true;
1456 } catch (NullPointerException e) {
1457 Slog.w(TAG, "failed parsing " + e);
1458 } catch (NumberFormatException e) {
1459 Slog.w(TAG, "failed parsing " + e);
1460 } catch (XmlPullParserException e) {
1461 Slog.w(TAG, "failed parsing " + e);
1462 } catch (IOException e) {
1463 Slog.w(TAG, "failed parsing " + e);
1464 } catch (IndexOutOfBoundsException e) {
1465 Slog.w(TAG, "failed parsing " + e);
1466 }
1467
1468 if (success) {
1469 // delete any hosts that didn't manage to get connected (should happen)
1470 // if it matters, they'll be reconnected.
1471 for (int i = mHosts.size() - 1; i >= 0; i--) {
1472 pruneHostLocked(mHosts.get(i));
1473 }
1474 } else {
1475 // failed reading, clean up
1476 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1477
1478 mAppWidgetIds.clear();
1479 mHosts.clear();
1480 final int N = mInstalledProviders.size();
1481 for (int i = 0; i < N; i++) {
1482 mInstalledProviders.get(i).instances.clear();
1483 }
1484 }
1485 }
1486
1487 AtomicFile savedStateFile() {
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001488 File dir = new File("/data/system/users/" + mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001489 File settingsFile = new File(dir, SETTINGS_FILENAME);
1490 if (!dir.exists()) {
1491 dir.mkdirs();
Amith Yamasani67cf7d32012-02-16 14:31:23 -08001492 if (mUserId == 0) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001493 // Migrate old data
1494 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1495 // Method doesn't throw an exception on failure. Ignore any errors
1496 // in moving the file (like non-existence)
1497 oldFile.renameTo(settingsFile);
1498 }
1499 }
1500 return new AtomicFile(settingsFile);
1501 }
1502
1503 void addProvidersForPackageLocked(String pkgName) {
1504 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1505 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001506 List<ResolveInfo> broadcastReceivers;
1507 try {
1508 broadcastReceivers = mPm.queryIntentReceivers(intent,
1509 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1510 PackageManager.GET_META_DATA, mUserId);
1511 } catch (RemoteException re) {
1512 // Shouldn't happen, local call
1513 return;
1514 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001515 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1516 for (int i = 0; i < N; i++) {
1517 ResolveInfo ri = broadcastReceivers.get(i);
1518 ActivityInfo ai = ri.activityInfo;
1519 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1520 continue;
1521 }
1522 if (pkgName.equals(ai.packageName)) {
1523 addProviderLocked(ri);
1524 }
1525 }
1526 }
1527
1528 void updateProvidersForPackageLocked(String pkgName) {
1529 HashSet<String> keep = new HashSet<String>();
1530 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1531 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001532 List<ResolveInfo> broadcastReceivers;
1533 try {
1534 broadcastReceivers = mPm.queryIntentReceivers(intent,
1535 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1536 PackageManager.GET_META_DATA, mUserId);
1537 } catch (RemoteException re) {
1538 // Shouldn't happen, local call
1539 return;
1540 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001541
1542 // add the missing ones and collect which ones to keep
1543 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1544 for (int i = 0; i < N; i++) {
1545 ResolveInfo ri = broadcastReceivers.get(i);
1546 ActivityInfo ai = ri.activityInfo;
1547 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1548 continue;
1549 }
1550 if (pkgName.equals(ai.packageName)) {
1551 ComponentName component = new ComponentName(ai.packageName, ai.name);
1552 Provider p = lookupProviderLocked(component);
1553 if (p == null) {
1554 if (addProviderLocked(ri)) {
1555 keep.add(ai.name);
1556 }
1557 } else {
1558 Provider parsed = parseProviderInfoXml(component, ri);
1559 if (parsed != null) {
1560 keep.add(ai.name);
1561 // Use the new AppWidgetProviderInfo.
1562 p.info = parsed.info;
1563 // If it's enabled
1564 final int M = p.instances.size();
1565 if (M > 0) {
1566 int[] appWidgetIds = getAppWidgetIds(p);
1567 // Reschedule for the new updatePeriodMillis (don't worry about handling
1568 // it specially if updatePeriodMillis didn't change because we just sent
1569 // an update, and the next one will be updatePeriodMillis from now).
1570 cancelBroadcasts(p);
1571 registerForBroadcastsLocked(p, appWidgetIds);
1572 // If it's currently showing, call back with the new
1573 // AppWidgetProviderInfo.
1574 for (int j = 0; j < M; j++) {
1575 AppWidgetId id = p.instances.get(j);
1576 id.views = null;
1577 if (id.host != null && id.host.callbacks != null) {
1578 try {
1579 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1580 } catch (RemoteException ex) {
1581 // It failed; remove the callback. No need to prune because
1582 // we know that this host is still referenced by this
1583 // instance.
1584 id.host.callbacks = null;
1585 }
1586 }
1587 }
1588 // Now that we've told the host, push out an update.
1589 sendUpdateIntentLocked(p, appWidgetIds);
1590 }
1591 }
1592 }
1593 }
1594 }
1595
1596 // prune the ones we don't want to keep
1597 N = mInstalledProviders.size();
1598 for (int i = N - 1; i >= 0; i--) {
1599 Provider p = mInstalledProviders.get(i);
1600 if (pkgName.equals(p.info.provider.getPackageName())
1601 && !keep.contains(p.info.provider.getClassName())) {
1602 removeProviderLocked(i, p);
1603 }
1604 }
1605 }
1606
1607 void removeProvidersForPackageLocked(String pkgName) {
1608 int N = mInstalledProviders.size();
1609 for (int i = N - 1; i >= 0; i--) {
1610 Provider p = mInstalledProviders.get(i);
1611 if (pkgName.equals(p.info.provider.getPackageName())) {
1612 removeProviderLocked(i, p);
1613 }
1614 }
1615
1616 // Delete the hosts for this package too
1617 //
1618 // By now, we have removed any AppWidgets that were in any hosts here,
1619 // so we don't need to worry about sending DISABLE broadcasts to them.
1620 N = mHosts.size();
1621 for (int i = N - 1; i >= 0; i--) {
1622 Host host = mHosts.get(i);
1623 if (pkgName.equals(host.packageName)) {
1624 deleteHostLocked(host);
1625 }
1626 }
1627 }
1628}