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