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