blob: e77f8cf24bccd9985d6e85014719f6d55e7bae28 [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;
Jeff Browna8b9def2012-07-23 14:22:49 -070039import android.graphics.Point;
Amith Yamasani742a6712011-05-04 14:49:28 -070040import android.net.Uri;
41import android.os.Binder;
42import android.os.Bundle;
Amith Yamasani61f57372012-08-31 12:12:28 -070043import android.os.Environment;
Amith Yamasani742a6712011-05-04 14:49:28 -070044import android.os.IBinder;
Jim Millerf229e4d2012-09-12 20:32:50 -070045import android.os.Process;
Amith Yamasani742a6712011-05-04 14:49:28 -070046import android.os.RemoteException;
47import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070048import android.os.UserHandle;
Dianne Hackborn39606a02012-07-31 17:54:35 -070049import android.util.AtomicFile;
Amith Yamasani742a6712011-05-04 14:49:28 -070050import android.util.AttributeSet;
51import android.util.Log;
52import android.util.Pair;
53import android.util.Slog;
54import android.util.TypedValue;
55import android.util.Xml;
Jeff Browna8b9def2012-07-23 14:22:49 -070056import android.view.Display;
Adam Cohen311c79c2012-05-10 14:44:38 -070057import android.view.WindowManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070058import android.widget.RemoteViews;
59
60import com.android.internal.appwidget.IAppWidgetHost;
Amith Yamasani742a6712011-05-04 14:49:28 -070061import com.android.internal.util.FastXmlSerializer;
62import com.android.internal.widget.IRemoteViewsAdapterConnection;
63import com.android.internal.widget.IRemoteViewsFactory;
Amith Yamasani742a6712011-05-04 14:49:28 -070064
65import org.xmlpull.v1.XmlPullParser;
66import org.xmlpull.v1.XmlPullParserException;
67import org.xmlpull.v1.XmlSerializer;
68
69import java.io.File;
70import java.io.FileDescriptor;
71import java.io.FileInputStream;
72import java.io.FileNotFoundException;
73import java.io.FileOutputStream;
74import java.io.IOException;
75import java.io.PrintWriter;
76import java.util.ArrayList;
77import java.util.HashMap;
78import java.util.HashSet;
79import java.util.Iterator;
80import java.util.List;
81import java.util.Locale;
82import java.util.Set;
83
84class AppWidgetServiceImpl {
85
86 private static final String TAG = "AppWidgetServiceImpl";
87 private static final String SETTINGS_FILENAME = "appwidgets.xml";
88 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
89
90 /*
91 * When identifying a Host or Provider based on the calling process, use the uid field. When
92 * identifying a Host or Provider based on a package manager broadcast, use the package given.
93 */
94
95 static class Provider {
96 int uid;
97 AppWidgetProviderInfo info;
98 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
99 PendingIntent broadcast;
100 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
101
102 int tag; // for use while saving state (the index)
103 }
104
105 static class Host {
106 int uid;
107 int hostId;
108 String packageName;
109 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
110 IAppWidgetHost callbacks;
111 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
112
113 int tag; // for use while saving state (the index)
114 }
115
116 static class AppWidgetId {
117 int appWidgetId;
118 Provider provider;
119 RemoteViews views;
Adam Cohend2097eb2012-05-01 18:10:28 -0700120 Bundle options;
Amith Yamasani742a6712011-05-04 14:49:28 -0700121 Host host;
122 }
123
124 /**
125 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
126 * needs to be a static inner class since a reference to the ServiceConnection is held globally
127 * and may lead us to leak AppWidgetService instances (if there were more than one).
128 */
129 static class ServiceConnectionProxy implements ServiceConnection {
130 private final IBinder mConnectionCb;
131
132 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
133 mConnectionCb = connectionCb;
134 }
135
136 public void onServiceConnected(ComponentName name, IBinder service) {
137 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
138 .asInterface(mConnectionCb);
139 try {
140 cb.onServiceConnected(service);
141 } catch (Exception e) {
142 e.printStackTrace();
143 }
144 }
145
146 public void onServiceDisconnected(ComponentName name) {
147 disconnect();
148 }
149
150 public void disconnect() {
151 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
152 .asInterface(mConnectionCb);
153 try {
154 cb.onServiceDisconnected();
155 } catch (Exception e) {
156 e.printStackTrace();
157 }
158 }
159 }
160
161 // Manages active connections to RemoteViewsServices
162 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
163 // Manages persistent references to RemoteViewsServices from different App Widgets
164 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
165
166 Context mContext;
167 Locale mLocale;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700168 IPackageManager mPm;
Amith Yamasani742a6712011-05-04 14:49:28 -0700169 AlarmManager mAlarmManager;
170 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
171 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
172 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
173 ArrayList<Host> mHosts = new ArrayList<Host>();
Michael Jurka61a5b012012-04-13 10:39:45 -0700174 // set of package names
175 HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
Amith Yamasani742a6712011-05-04 14:49:28 -0700176 boolean mSafeMode;
177 int mUserId;
178 boolean mStateLoaded;
Adam Cohen311c79c2012-05-10 14:44:38 -0700179 int mMaxWidgetBitmapMemory;
Amith Yamasani742a6712011-05-04 14:49:28 -0700180
181 // These are for debugging only -- widgets are going missing in some rare instances
182 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
183 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
184
185 AppWidgetServiceImpl(Context context, int userId) {
186 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700187 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700188 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
189 mUserId = userId;
Adam Cohen311c79c2012-05-10 14:44:38 -0700190 computeMaximumWidgetBitmapMemory();
191 }
192
193 void computeMaximumWidgetBitmapMemory() {
194 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Jeff Browna8b9def2012-07-23 14:22:49 -0700195 Display display = wm.getDefaultDisplay();
196 Point size = new Point();
197 display.getRealSize(size);
Winson Chunge92aad42012-06-22 14:11:47 -0700198 // Cap memory usage at 1.5 times the size of the display
199 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
Jeff Browna8b9def2012-07-23 14:22:49 -0700200 mMaxWidgetBitmapMemory = 6 * size.x * size.y;
Amith Yamasani742a6712011-05-04 14:49:28 -0700201 }
202
203 public void systemReady(boolean safeMode) {
204 mSafeMode = safeMode;
205
206 synchronized (mAppWidgetIds) {
207 ensureStateLoadedLocked();
208 }
209 }
210
211 void onConfigurationChanged() {
212 Locale revised = Locale.getDefault();
213 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
214 mLocale = revised;
215
216 synchronized (mAppWidgetIds) {
217 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700218 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
219 // list of installed providers and skip providers that we don't need to update.
220 // Also note that remove the provider does not clear the Provider component data.
221 ArrayList<Provider> installedProviders =
222 new ArrayList<Provider>(mInstalledProviders);
223 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
224 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700225 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700226 Provider p = installedProviders.get(i);
227 ComponentName cn = p.info.provider;
228 if (!removedProviders.contains(cn)) {
229 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
230 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700231 }
232 saveStateLocked();
233 }
234 }
235 }
236
237 void onBroadcastReceived(Intent intent) {
238 final String action = intent.getAction();
239 boolean added = false;
240 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700241 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700242 String pkgList[] = null;
243 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
244 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
245 added = true;
246 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
247 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
248 added = false;
249 } else {
250 Uri uri = intent.getData();
251 if (uri == null) {
252 return;
253 }
254 String pkgName = uri.getSchemeSpecificPart();
255 if (pkgName == null) {
256 return;
257 }
258 pkgList = new String[] { pkgName };
259 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
260 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
261 }
262 if (pkgList == null || pkgList.length == 0) {
263 return;
264 }
265 if (added || changed) {
266 synchronized (mAppWidgetIds) {
267 ensureStateLoadedLocked();
268 Bundle extras = intent.getExtras();
269 if (changed
270 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
271 for (String pkgName : pkgList) {
272 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700273 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700274 }
275 } else {
276 // The package was just added
277 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700278 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700279 }
280 }
281 saveStateLocked();
282 }
283 } else {
284 Bundle extras = intent.getExtras();
285 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
286 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
287 } else {
288 synchronized (mAppWidgetIds) {
289 ensureStateLoadedLocked();
290 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700291 providersModified |= removeProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700292 saveStateLocked();
293 }
294 }
295 }
296 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700297
298 if (providersModified) {
299 // If the set of providers has been modified, notify each active AppWidgetHost
300 synchronized (mAppWidgetIds) {
301 ensureStateLoadedLocked();
302 notifyHostsForProvidersChangedLocked();
303 }
304 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700305 }
306
307 private void dumpProvider(Provider p, int index, PrintWriter pw) {
308 AppWidgetProviderInfo info = p.info;
309 pw.print(" ["); pw.print(index); pw.print("] provider ");
310 pw.print(info.provider.flattenToShortString());
311 pw.println(':');
312 pw.print(" min=("); pw.print(info.minWidth);
313 pw.print("x"); pw.print(info.minHeight);
314 pw.print(") minResize=("); pw.print(info.minResizeWidth);
315 pw.print("x"); pw.print(info.minResizeHeight);
316 pw.print(") updatePeriodMillis=");
317 pw.print(info.updatePeriodMillis);
318 pw.print(" resizeMode=");
319 pw.print(info.resizeMode);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700320 pw.print(info.widgetCategory);
321 pw.print(info.widgetFeatures);
Amith Yamasani742a6712011-05-04 14:49:28 -0700322 pw.print(" autoAdvanceViewId=");
323 pw.print(info.autoAdvanceViewId);
324 pw.print(" initialLayout=#");
325 pw.print(Integer.toHexString(info.initialLayout));
326 pw.print(" zombie="); pw.println(p.zombie);
327 }
328
329 private void dumpHost(Host host, int index, PrintWriter pw) {
330 pw.print(" ["); pw.print(index); pw.print("] hostId=");
331 pw.print(host.hostId); pw.print(' ');
332 pw.print(host.packageName); pw.print('/');
333 pw.print(host.uid); pw.println(':');
334 pw.print(" callbacks="); pw.println(host.callbacks);
335 pw.print(" instances.size="); pw.print(host.instances.size());
336 pw.print(" zombie="); pw.println(host.zombie);
337 }
338
339 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
340 pw.print(" ["); pw.print(index); pw.print("] id=");
341 pw.println(id.appWidgetId);
342 pw.print(" hostId=");
343 pw.print(id.host.hostId); pw.print(' ');
344 pw.print(id.host.packageName); pw.print('/');
345 pw.println(id.host.uid);
346 if (id.provider != null) {
347 pw.print(" provider=");
348 pw.println(id.provider.info.provider.flattenToShortString());
349 }
350 if (id.host != null) {
351 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
352 }
353 if (id.views != null) {
354 pw.print(" views="); pw.println(id.views);
355 }
356 }
357
358 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
359 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
360 != PackageManager.PERMISSION_GRANTED) {
361 pw.println("Permission Denial: can't dump from from pid="
362 + Binder.getCallingPid()
363 + ", uid=" + Binder.getCallingUid());
364 return;
365 }
366
367 synchronized (mAppWidgetIds) {
368 int N = mInstalledProviders.size();
369 pw.println("Providers:");
370 for (int i=0; i<N; i++) {
371 dumpProvider(mInstalledProviders.get(i), i, pw);
372 }
373
374 N = mAppWidgetIds.size();
375 pw.println(" ");
376 pw.println("AppWidgetIds:");
377 for (int i=0; i<N; i++) {
378 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
379 }
380
381 N = mHosts.size();
382 pw.println(" ");
383 pw.println("Hosts:");
384 for (int i=0; i<N; i++) {
385 dumpHost(mHosts.get(i), i, pw);
386 }
387
388 N = mDeletedProviders.size();
389 pw.println(" ");
390 pw.println("Deleted Providers:");
391 for (int i=0; i<N; i++) {
392 dumpProvider(mDeletedProviders.get(i), i, pw);
393 }
394
395 N = mDeletedHosts.size();
396 pw.println(" ");
397 pw.println("Deleted Hosts:");
398 for (int i=0; i<N; i++) {
399 dumpHost(mDeletedHosts.get(i), i, pw);
400 }
401 }
402 }
403
404 private void ensureStateLoadedLocked() {
405 if (!mStateLoaded) {
406 loadAppWidgetList();
407 loadStateLocked();
408 mStateLoaded = true;
409 }
410 }
411
412 public int allocateAppWidgetId(String packageName, int hostId) {
Jim Millerf229e4d2012-09-12 20:32:50 -0700413 int callingUid = enforceSystemOrCallingUid(packageName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700414 synchronized (mAppWidgetIds) {
415 ensureStateLoadedLocked();
416 int appWidgetId = mNextAppWidgetId++;
417
418 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
419
420 AppWidgetId id = new AppWidgetId();
421 id.appWidgetId = appWidgetId;
422 id.host = host;
423
424 host.instances.add(id);
425 mAppWidgetIds.add(id);
426
427 saveStateLocked();
428
429 return appWidgetId;
430 }
431 }
432
433 public void deleteAppWidgetId(int appWidgetId) {
434 synchronized (mAppWidgetIds) {
435 ensureStateLoadedLocked();
436 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
437 if (id != null) {
438 deleteAppWidgetLocked(id);
439 saveStateLocked();
440 }
441 }
442 }
443
444 public void deleteHost(int hostId) {
445 synchronized (mAppWidgetIds) {
446 ensureStateLoadedLocked();
447 int callingUid = Binder.getCallingUid();
448 Host host = lookupHostLocked(callingUid, hostId);
449 if (host != null) {
450 deleteHostLocked(host);
451 saveStateLocked();
452 }
453 }
454 }
455
456 public void deleteAllHosts() {
457 synchronized (mAppWidgetIds) {
458 ensureStateLoadedLocked();
459 int callingUid = Binder.getCallingUid();
460 final int N = mHosts.size();
461 boolean changed = false;
462 for (int i = N - 1; i >= 0; i--) {
463 Host host = mHosts.get(i);
464 if (host.uid == callingUid) {
465 deleteHostLocked(host);
466 changed = true;
467 }
468 }
469 if (changed) {
470 saveStateLocked();
471 }
472 }
473 }
474
475 void deleteHostLocked(Host host) {
476 final int N = host.instances.size();
477 for (int i = N - 1; i >= 0; i--) {
478 AppWidgetId id = host.instances.get(i);
479 deleteAppWidgetLocked(id);
480 }
481 host.instances.clear();
482 mHosts.remove(host);
483 mDeletedHosts.add(host);
484 // it's gone or going away, abruptly drop the callback connection
485 host.callbacks = null;
486 }
487
488 void deleteAppWidgetLocked(AppWidgetId id) {
489 // We first unbind all services that are bound to this id
490 unbindAppWidgetRemoteViewsServicesLocked(id);
491
492 Host host = id.host;
493 host.instances.remove(id);
494 pruneHostLocked(host);
495
496 mAppWidgetIds.remove(id);
497
498 Provider p = id.provider;
499 if (p != null) {
500 p.instances.remove(id);
501 if (!p.zombie) {
502 // send the broacast saying that this appWidgetId has been deleted
503 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
504 intent.setComponent(p.info.provider);
505 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700506 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700507 if (p.instances.size() == 0) {
508 // cancel the future updates
509 cancelBroadcasts(p);
510
511 // send the broacast saying that the provider is not in use any more
512 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
513 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700514 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700515 }
516 }
517 }
518 }
519
520 void cancelBroadcasts(Provider p) {
521 if (p.broadcast != null) {
522 mAlarmManager.cancel(p.broadcast);
523 long token = Binder.clearCallingIdentity();
524 try {
525 p.broadcast.cancel();
526 } finally {
527 Binder.restoreCallingIdentity(token);
528 }
529 p.broadcast = null;
530 }
531 }
532
Adam Cohen0aa2d422012-09-07 17:37:26 -0700533 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700534 final long ident = Binder.clearCallingIdentity();
535 try {
536 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700537 options = cloneIfLocalBinder(options);
Amith Yamasani742a6712011-05-04 14:49:28 -0700538 ensureStateLoadedLocked();
539 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
540 if (id == null) {
541 throw new IllegalArgumentException("bad appWidgetId");
542 }
543 if (id.provider != null) {
544 throw new IllegalArgumentException("appWidgetId " + appWidgetId
545 + " already bound to " + id.provider.info.provider);
546 }
547 Provider p = lookupProviderLocked(provider);
548 if (p == null) {
549 throw new IllegalArgumentException("not a appwidget provider: " + provider);
550 }
551 if (p.zombie) {
552 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
553 + " safe mode: " + provider);
554 }
555
Amith Yamasani742a6712011-05-04 14:49:28 -0700556 id.provider = p;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700557 if (options == null) {
558 options = new Bundle();
559 }
560 id.options = options;
561
562 // We need to provide a default value for the widget category if it is not specified
563 if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
564 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
565 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
566 }
567
Amith Yamasani742a6712011-05-04 14:49:28 -0700568 p.instances.add(id);
569 int instancesSize = p.instances.size();
570 if (instancesSize == 1) {
571 // tell the provider that it's ready
572 sendEnableIntentLocked(p);
573 }
574
575 // send an update now -- We need this update now, and just for this appWidgetId.
576 // It's less critical when the next one happens, so when we schedule the next one,
577 // we add updatePeriodMillis to its start time. That time will have some slop,
578 // but that's okay.
579 sendUpdateIntentLocked(p, new int[] { appWidgetId });
580
581 // schedule the future updates
582 registerForBroadcastsLocked(p, getAppWidgetIds(p));
583 saveStateLocked();
584 }
585 } finally {
586 Binder.restoreCallingIdentity(ident);
587 }
588 }
589
Adam Cohen0aa2d422012-09-07 17:37:26 -0700590 public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700591 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
592 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700593 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700594 }
595
596 public boolean bindAppWidgetIdIfAllowed(
Adam Cohen0aa2d422012-09-07 17:37:26 -0700597 String packageName, int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700598 try {
599 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
600 } catch (SecurityException se) {
601 if (!callerHasBindAppWidgetPermission(packageName)) {
602 return false;
603 }
604 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700605 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700606 return true;
607 }
608
609 private boolean callerHasBindAppWidgetPermission(String packageName) {
610 int callingUid = Binder.getCallingUid();
611 try {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700612 if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700613 return false;
614 }
615 } catch (Exception e) {
616 return false;
617 }
618 synchronized (mAppWidgetIds) {
619 ensureStateLoadedLocked();
620 return mPackagesWithBindWidgetPermission.contains(packageName);
621 }
622 }
623
624 public boolean hasBindAppWidgetPermission(String packageName) {
625 mContext.enforceCallingPermission(
626 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
627 "hasBindAppWidgetPermission packageName=" + packageName);
628
629 synchronized (mAppWidgetIds) {
630 ensureStateLoadedLocked();
631 return mPackagesWithBindWidgetPermission.contains(packageName);
632 }
633 }
634
635 public void setBindAppWidgetPermission(String packageName, boolean permission) {
636 mContext.enforceCallingPermission(
637 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
638 "setBindAppWidgetPermission packageName=" + packageName);
639
640 synchronized (mAppWidgetIds) {
641 ensureStateLoadedLocked();
642 if (permission) {
643 mPackagesWithBindWidgetPermission.add(packageName);
644 } else {
645 mPackagesWithBindWidgetPermission.remove(packageName);
646 }
647 }
648 saveStateLocked();
649 }
650
Amith Yamasani742a6712011-05-04 14:49:28 -0700651 // Binds to a specific RemoteViewsService
652 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
653 synchronized (mAppWidgetIds) {
654 ensureStateLoadedLocked();
655 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
656 if (id == null) {
657 throw new IllegalArgumentException("bad appWidgetId");
658 }
659 final ComponentName componentName = intent.getComponent();
660 try {
Amith Yamasani98edc952012-09-25 14:09:27 -0700661 final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
662 PackageManager.GET_PERMISSIONS, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700663 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
664 throw new SecurityException("Selected service does not require "
665 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
666 }
Amith Yamasani98edc952012-09-25 14:09:27 -0700667 } catch (RemoteException e) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700668 throw new IllegalArgumentException("Unknown component " + componentName);
669 }
670
671 // If there is already a connection made for this service intent, then disconnect from
672 // that first. (This does not allow multiple connections to the same service under
673 // the same key)
674 ServiceConnectionProxy conn = null;
675 FilterComparison fc = new FilterComparison(intent);
676 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
677 if (mBoundRemoteViewsServices.containsKey(key)) {
678 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
679 conn.disconnect();
680 mContext.unbindService(conn);
681 mBoundRemoteViewsServices.remove(key);
682 }
683
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700684 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700685 // Bind to the RemoteViewsService (which will trigger a callback to the
686 // RemoteViewsAdapter.onServiceConnected())
687 final long token = Binder.clearCallingIdentity();
688 try {
689 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800690 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700691 mBoundRemoteViewsServices.put(key, conn);
692 } finally {
693 Binder.restoreCallingIdentity(token);
694 }
695
696 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
697 // when we can call back to the RemoteViewsService later to destroy associated
698 // factories.
699 incrementAppWidgetServiceRefCount(appWidgetId, fc);
700 }
701 }
702
703 // Unbinds from a specific RemoteViewsService
704 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
705 synchronized (mAppWidgetIds) {
706 ensureStateLoadedLocked();
707 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
708 // RemoteViewsAdapter)
709 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
710 intent));
711 if (mBoundRemoteViewsServices.containsKey(key)) {
712 // We don't need to use the appWidgetId until after we are sure there is something
713 // to unbind. Note that this may mask certain issues with apps calling unbind()
714 // more than necessary.
715 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
716 if (id == null) {
717 throw new IllegalArgumentException("bad appWidgetId");
718 }
719
720 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
721 .get(key);
722 conn.disconnect();
723 mContext.unbindService(conn);
724 mBoundRemoteViewsServices.remove(key);
725 } else {
726 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
727 }
728 }
729 }
730
731 // Unbinds from a RemoteViewsService when we delete an app widget
732 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
733 int appWidgetId = id.appWidgetId;
734 // Unbind all connections to Services bound to this AppWidgetId
735 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
736 .iterator();
737 while (it.hasNext()) {
738 final Pair<Integer, Intent.FilterComparison> key = it.next();
739 if (key.first.intValue() == appWidgetId) {
740 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
741 .get(key);
742 conn.disconnect();
743 mContext.unbindService(conn);
744 it.remove();
745 }
746 }
747
748 // Check if we need to destroy any services (if no other app widgets are
749 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800750 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700751 }
752
753 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800754 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700755 final ServiceConnection conn = new ServiceConnection() {
756 @Override
757 public void onServiceConnected(ComponentName name, IBinder service) {
758 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
759 try {
760 cb.onDestroy(intent);
761 } catch (RemoteException e) {
762 e.printStackTrace();
763 } catch (RuntimeException e) {
764 e.printStackTrace();
765 }
766 mContext.unbindService(this);
767 }
768
769 @Override
770 public void onServiceDisconnected(android.content.ComponentName name) {
771 // Do nothing
772 }
773 };
774
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700775 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700776 // Bind to the service and remove the static intent->factory mapping in the
777 // RemoteViewsService.
778 final long token = Binder.clearCallingIdentity();
779 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800780 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700781 } finally {
782 Binder.restoreCallingIdentity(token);
783 }
784 }
785
786 // Adds to the ref-count for a given RemoteViewsService intent
787 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
788 HashSet<Integer> appWidgetIds = null;
789 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
790 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
791 } else {
792 appWidgetIds = new HashSet<Integer>();
793 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
794 }
795 appWidgetIds.add(appWidgetId);
796 }
797
798 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
799 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800800 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700801 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
802 while (it.hasNext()) {
803 final FilterComparison key = it.next();
804 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800805 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700806 // If we have removed the last app widget referencing this service, then we
807 // should destroy it and remove it from this set
808 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800809 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700810 it.remove();
811 }
812 }
813 }
814 }
815
816 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
817 synchronized (mAppWidgetIds) {
818 ensureStateLoadedLocked();
819 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
820 if (id != null && id.provider != null && !id.provider.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700821 return cloneIfLocalBinder(id.provider.info);
Amith Yamasani742a6712011-05-04 14:49:28 -0700822 }
823 return null;
824 }
825 }
826
827 public RemoteViews getAppWidgetViews(int appWidgetId) {
828 synchronized (mAppWidgetIds) {
829 ensureStateLoadedLocked();
830 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
831 if (id != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700832 return cloneIfLocalBinder(id.views);
Amith Yamasani742a6712011-05-04 14:49:28 -0700833 }
834 return null;
835 }
836 }
837
838 public List<AppWidgetProviderInfo> getInstalledProviders() {
839 synchronized (mAppWidgetIds) {
840 ensureStateLoadedLocked();
841 final int N = mInstalledProviders.size();
842 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
843 for (int i = 0; i < N; i++) {
844 Provider p = mInstalledProviders.get(i);
845 if (!p.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700846 result.add(cloneIfLocalBinder(p.info));
Amith Yamasani742a6712011-05-04 14:49:28 -0700847 }
848 }
849 return result;
850 }
851 }
852
853 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
854 if (appWidgetIds == null) {
855 return;
856 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700857
Adam Cohenf08a8b72012-07-16 12:02:10 -0700858 int bitmapMemoryUsage = 0;
859 if (views != null) {
860 bitmapMemoryUsage = views.estimateMemoryUsage();
861 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700862 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
863 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
864 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
865 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
866 " fill the device's screen once.");
867 }
868
Amith Yamasani742a6712011-05-04 14:49:28 -0700869 if (appWidgetIds.length == 0) {
870 return;
871 }
872 final int N = appWidgetIds.length;
873
874 synchronized (mAppWidgetIds) {
875 ensureStateLoadedLocked();
876 for (int i = 0; i < N; i++) {
877 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
878 updateAppWidgetInstanceLocked(id, views);
879 }
880 }
881 }
882
Adam Cohend2097eb2012-05-01 18:10:28 -0700883 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700884 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700885 options = cloneIfLocalBinder(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700886 ensureStateLoadedLocked();
887 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
888
889 if (id == null) {
890 return;
891 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700892
Adam Cohene8724c82012-04-19 17:11:40 -0700893 Provider p = id.provider;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700894 // Merge the options
895 id.options.putAll(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700896
897 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700898 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700899 intent.setComponent(p.info.provider);
900 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700901 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700902 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Adam Cohen0aa2d422012-09-07 17:37:26 -0700903 saveStateLocked();
Adam Cohene8724c82012-04-19 17:11:40 -0700904 }
905 }
906
Adam Cohend2097eb2012-05-01 18:10:28 -0700907 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -0700908 synchronized (mAppWidgetIds) {
909 ensureStateLoadedLocked();
910 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700911 if (id != null && id.options != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700912 return cloneIfLocalBinder(id.options);
Adam Cohene8724c82012-04-19 17:11:40 -0700913 } else {
914 return Bundle.EMPTY;
915 }
916 }
917 }
918
Amith Yamasani742a6712011-05-04 14:49:28 -0700919 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
920 if (appWidgetIds == null) {
921 return;
922 }
923 if (appWidgetIds.length == 0) {
924 return;
925 }
926 final int N = appWidgetIds.length;
927
928 synchronized (mAppWidgetIds) {
929 ensureStateLoadedLocked();
930 for (int i = 0; i < N; i++) {
931 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
932 updateAppWidgetInstanceLocked(id, views, true);
933 }
934 }
935 }
936
937 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
938 if (appWidgetIds == null) {
939 return;
940 }
941 if (appWidgetIds.length == 0) {
942 return;
943 }
944 final int N = appWidgetIds.length;
945
946 synchronized (mAppWidgetIds) {
947 ensureStateLoadedLocked();
948 for (int i = 0; i < N; i++) {
949 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
950 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
951 }
952 }
953 }
954
955 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
956 synchronized (mAppWidgetIds) {
957 ensureStateLoadedLocked();
958 Provider p = lookupProviderLocked(provider);
959 if (p == null) {
960 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
961 return;
962 }
963 ArrayList<AppWidgetId> instances = p.instances;
964 final int callingUid = Binder.getCallingUid();
965 final int N = instances.size();
966 for (int i = 0; i < N; i++) {
967 AppWidgetId id = instances.get(i);
968 if (canAccessAppWidgetId(id, callingUid)) {
969 updateAppWidgetInstanceLocked(id, views);
970 }
971 }
972 }
973 }
974
975 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
976 updateAppWidgetInstanceLocked(id, views, false);
977 }
978
979 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
980 // allow for stale appWidgetIds and other badness
981 // lookup also checks that the calling process can access the appWidgetId
982 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
983 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
984
Adam Cohenfbe44b72012-09-19 20:36:23 -0700985 if (!isPartialUpdate) {
986 // For a full update we replace the RemoteViews completely.
Amith Yamasani742a6712011-05-04 14:49:28 -0700987 id.views = views;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700988 } else {
989 // For a partial update, we merge the new RemoteViews with the old.
990 id.views.mergeRemoteViews(views);
991 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700992
993 // is anyone listening?
994 if (id.host.callbacks != null) {
995 try {
996 // the lock is held, but this is a oneway call
997 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
998 } catch (RemoteException e) {
999 // It failed; remove the callback. No need to prune because
1000 // we know that this host is still referenced by this instance.
1001 id.host.callbacks = null;
1002 }
1003 }
1004 }
1005 }
1006
1007 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1008 // allow for stale appWidgetIds and other badness
1009 // lookup also checks that the calling process can access the appWidgetId
1010 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1011 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1012 // is anyone listening?
1013 if (id.host.callbacks != null) {
1014 try {
1015 // the lock is held, but this is a oneway call
1016 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1017 } catch (RemoteException e) {
1018 // It failed; remove the callback. No need to prune because
1019 // we know that this host is still referenced by this instance.
1020 id.host.callbacks = null;
1021 }
1022 }
1023
1024 // If the host is unavailable, then we call the associated
1025 // RemoteViewsFactory.onDataSetChanged() directly
1026 if (id.host.callbacks == null) {
1027 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1028 for (FilterComparison key : keys) {
1029 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1030 Intent intent = key.getIntent();
1031
1032 final ServiceConnection conn = new ServiceConnection() {
1033 @Override
1034 public void onServiceConnected(ComponentName name, IBinder service) {
1035 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1036 .asInterface(service);
1037 try {
1038 cb.onDataSetChangedAsync();
1039 } catch (RemoteException e) {
1040 e.printStackTrace();
1041 } catch (RuntimeException e) {
1042 e.printStackTrace();
1043 }
1044 mContext.unbindService(this);
1045 }
1046
1047 @Override
1048 public void onServiceDisconnected(android.content.ComponentName name) {
1049 // Do nothing
1050 }
1051 };
1052
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001053 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001054 // Bind to the service and call onDataSetChanged()
1055 final long token = Binder.clearCallingIdentity();
1056 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001057 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001058 } finally {
1059 Binder.restoreCallingIdentity(token);
1060 }
1061 }
1062 }
1063 }
1064 }
1065 }
1066
Adam Cohen3ff2d862012-09-26 14:07:57 -07001067 private boolean isLocalBinder() {
1068 return Process.myPid() == Binder.getCallingPid();
1069 }
1070
1071 private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1072 if (isLocalBinder() && rv != null) {
1073 return rv.clone();
1074 }
1075 return rv;
1076 }
1077
1078 private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
1079 if (isLocalBinder() && info != null) {
1080 return info.clone();
1081 }
1082 return info;
1083 }
1084
1085 private Bundle cloneIfLocalBinder(Bundle bundle) {
1086 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
1087 // if we start adding objects to the options. Further, it would only be an issue if keyguard
1088 // used such options.
1089 if (isLocalBinder() && bundle != null) {
1090 return (Bundle) bundle.clone();
1091 }
1092 return bundle;
1093 }
1094
Amith Yamasani742a6712011-05-04 14:49:28 -07001095 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1096 List<RemoteViews> updatedViews) {
1097 int callingUid = enforceCallingUid(packageName);
1098 synchronized (mAppWidgetIds) {
1099 ensureStateLoadedLocked();
1100 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1101 host.callbacks = callbacks;
1102
1103 updatedViews.clear();
1104
1105 ArrayList<AppWidgetId> instances = host.instances;
1106 int N = instances.size();
1107 int[] updatedIds = new int[N];
1108 for (int i = 0; i < N; i++) {
1109 AppWidgetId id = instances.get(i);
1110 updatedIds[i] = id.appWidgetId;
Adam Cohen3ff2d862012-09-26 14:07:57 -07001111 updatedViews.add(cloneIfLocalBinder(id.views));
Amith Yamasani742a6712011-05-04 14:49:28 -07001112 }
1113 return updatedIds;
1114 }
1115 }
1116
1117 public void stopListening(int hostId) {
1118 synchronized (mAppWidgetIds) {
1119 ensureStateLoadedLocked();
1120 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1121 if (host != null) {
1122 host.callbacks = null;
1123 pruneHostLocked(host);
1124 }
1125 }
1126 }
1127
1128 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1129 if (id.host.uid == callingUid) {
1130 // Apps hosting the AppWidget have access to it.
1131 return true;
1132 }
1133 if (id.provider != null && id.provider.uid == callingUid) {
1134 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1135 return true;
1136 }
1137 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1138 // Apps that can bind have access to all appWidgetIds.
1139 return true;
1140 }
1141 // Nobody else can access it.
1142 return false;
1143 }
1144
1145 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1146 int callingUid = Binder.getCallingUid();
1147 final int N = mAppWidgetIds.size();
1148 for (int i = 0; i < N; i++) {
1149 AppWidgetId id = mAppWidgetIds.get(i);
1150 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1151 return id;
1152 }
1153 }
1154 return null;
1155 }
1156
1157 Provider lookupProviderLocked(ComponentName provider) {
1158 final int N = mInstalledProviders.size();
1159 for (int i = 0; i < N; i++) {
1160 Provider p = mInstalledProviders.get(i);
1161 if (p.info.provider.equals(provider)) {
1162 return p;
1163 }
1164 }
1165 return null;
1166 }
1167
1168 Host lookupHostLocked(int uid, int hostId) {
1169 final int N = mHosts.size();
1170 for (int i = 0; i < N; i++) {
1171 Host h = mHosts.get(i);
1172 if (h.uid == uid && h.hostId == hostId) {
1173 return h;
1174 }
1175 }
1176 return null;
1177 }
1178
1179 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1180 final int N = mHosts.size();
1181 for (int i = 0; i < N; i++) {
1182 Host h = mHosts.get(i);
1183 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1184 return h;
1185 }
1186 }
1187 Host host = new Host();
1188 host.packageName = packageName;
1189 host.uid = uid;
1190 host.hostId = hostId;
1191 mHosts.add(host);
1192 return host;
1193 }
1194
1195 void pruneHostLocked(Host host) {
1196 if (host.instances.size() == 0 && host.callbacks == null) {
1197 mHosts.remove(host);
1198 }
1199 }
1200
1201 void loadAppWidgetList() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001202 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001203 try {
1204 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1205 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1206 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001207
Amith Yamasani483f3b02012-03-13 16:08:00 -07001208 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1209 for (int i = 0; i < N; i++) {
1210 ResolveInfo ri = broadcastReceivers.get(i);
1211 addProviderLocked(ri);
1212 }
1213 } catch (RemoteException re) {
1214 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001215 }
1216 }
1217
1218 boolean addProviderLocked(ResolveInfo ri) {
1219 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1220 return false;
1221 }
1222 if (!ri.activityInfo.isEnabled()) {
1223 return false;
1224 }
1225 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1226 ri.activityInfo.name), ri);
1227 if (p != null) {
1228 mInstalledProviders.add(p);
1229 return true;
1230 } else {
1231 return false;
1232 }
1233 }
1234
1235 void removeProviderLocked(int index, Provider p) {
1236 int N = p.instances.size();
1237 for (int i = 0; i < N; i++) {
1238 AppWidgetId id = p.instances.get(i);
1239 // Call back with empty RemoteViews
1240 updateAppWidgetInstanceLocked(id, null);
1241 // Stop telling the host about updates for this from now on
1242 cancelBroadcasts(p);
1243 // clear out references to this appWidgetId
1244 id.host.instances.remove(id);
1245 mAppWidgetIds.remove(id);
1246 id.provider = null;
1247 pruneHostLocked(id.host);
1248 id.host = null;
1249 }
1250 p.instances.clear();
1251 mInstalledProviders.remove(index);
1252 mDeletedProviders.add(p);
1253 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1254 cancelBroadcasts(p);
1255 }
1256
1257 void sendEnableIntentLocked(Provider p) {
1258 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1259 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001260 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001261 }
1262
1263 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1264 if (appWidgetIds != null && appWidgetIds.length > 0) {
1265 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1266 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1267 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001268 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001269 }
1270 }
1271
1272 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1273 if (p.info.updatePeriodMillis > 0) {
1274 // if this is the first instance, set the alarm. otherwise,
1275 // rely on the fact that we've already set it and that
1276 // PendingIntent.getBroadcast will update the extras.
1277 boolean alreadyRegistered = p.broadcast != null;
1278 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1279 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1280 intent.setComponent(p.info.provider);
1281 long token = Binder.clearCallingIdentity();
1282 try {
1283 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1284 PendingIntent.FLAG_UPDATE_CURRENT);
1285 } finally {
1286 Binder.restoreCallingIdentity(token);
1287 }
1288 if (!alreadyRegistered) {
1289 long period = p.info.updatePeriodMillis;
1290 if (period < MIN_UPDATE_PERIOD) {
1291 period = MIN_UPDATE_PERIOD;
1292 }
1293 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1294 .elapsedRealtime()
1295 + period, period, p.broadcast);
1296 }
1297 }
1298 }
1299
1300 static int[] getAppWidgetIds(Provider p) {
1301 int instancesSize = p.instances.size();
1302 int appWidgetIds[] = new int[instancesSize];
1303 for (int i = 0; i < instancesSize; i++) {
1304 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1305 }
1306 return appWidgetIds;
1307 }
1308
1309 public int[] getAppWidgetIds(ComponentName provider) {
1310 synchronized (mAppWidgetIds) {
1311 ensureStateLoadedLocked();
1312 Provider p = lookupProviderLocked(provider);
1313 if (p != null && Binder.getCallingUid() == p.uid) {
1314 return getAppWidgetIds(p);
1315 } else {
1316 return new int[0];
1317 }
1318 }
1319 }
1320
1321 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1322 Provider p = null;
1323
1324 ActivityInfo activityInfo = ri.activityInfo;
1325 XmlResourceParser parser = null;
1326 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001327 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001328 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1329 if (parser == null) {
1330 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1331 + " meta-data for " + "AppWidget provider '" + component + '\'');
1332 return null;
1333 }
1334
1335 AttributeSet attrs = Xml.asAttributeSet(parser);
1336
1337 int type;
1338 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1339 && type != XmlPullParser.START_TAG) {
1340 // drain whitespace, comments, etc.
1341 }
1342
1343 String nodeName = parser.getName();
1344 if (!"appwidget-provider".equals(nodeName)) {
1345 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1346 + " AppWidget provider '" + component + '\'');
1347 return null;
1348 }
1349
1350 p = new Provider();
1351 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1352 info.provider = component;
1353 p.uid = activityInfo.applicationInfo.uid;
1354
Amith Yamasani483f3b02012-03-13 16:08:00 -07001355 Resources res = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001356 .getResourcesForApplication(activityInfo.applicationInfo);
1357
1358 TypedArray sa = res.obtainAttributes(attrs,
1359 com.android.internal.R.styleable.AppWidgetProviderInfo);
1360
1361 // These dimensions has to be resolved in the application's context.
1362 // We simply send back the raw complex data, which will be
1363 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1364 TypedValue value = sa
1365 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1366 info.minWidth = value != null ? value.data : 0;
1367 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1368 info.minHeight = value != null ? value.data : 0;
1369 value = sa.peekValue(
1370 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1371 info.minResizeWidth = value != null ? value.data : info.minWidth;
1372 value = sa.peekValue(
1373 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1374 info.minResizeHeight = value != null ? value.data : info.minHeight;
1375 info.updatePeriodMillis = sa.getInt(
1376 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1377 info.initialLayout = sa.getResourceId(
1378 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001379 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1380 AppWidgetProviderInfo_initialKeyguardLayout, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001381 String className = sa
1382 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1383 if (className != null) {
1384 info.configure = new ComponentName(component.getPackageName(), className);
1385 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001386 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001387 info.icon = ri.getIconResource();
1388 info.previewImage = sa.getResourceId(
1389 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1390 info.autoAdvanceViewId = sa.getResourceId(
1391 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1392 info.resizeMode = sa.getInt(
1393 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1394 AppWidgetProviderInfo.RESIZE_NONE);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001395 info.widgetCategory = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001396 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001397 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
1398 info.widgetFeatures = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001399 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001400 AppWidgetProviderInfo.WIDGET_FEATURES_NONE);
Amith Yamasani742a6712011-05-04 14:49:28 -07001401
1402 sa.recycle();
1403 } catch (Exception e) {
1404 // Ok to catch Exception here, because anything going wrong because
1405 // of what a client process passes to us should not be fatal for the
1406 // system process.
1407 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1408 return null;
1409 } finally {
1410 if (parser != null)
1411 parser.close();
1412 }
1413 return p;
1414 }
1415
1416 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001417 PackageInfo pkgInfo = null;
1418 try {
1419 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1420 } catch (RemoteException re) {
1421 // Shouldn't happen, local call
1422 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001423 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1424 throw new PackageManager.NameNotFoundException();
1425 }
1426 return pkgInfo.applicationInfo.uid;
1427 }
1428
Jim Millerf229e4d2012-09-12 20:32:50 -07001429 int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1430 int callingUid = Binder.getCallingUid();
Michael Jurka03bdc8a2012-09-21 16:10:21 -07001431 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
Jim Millerf229e4d2012-09-12 20:32:50 -07001432 return callingUid;
1433 }
1434 return enforceCallingUid(packageName);
1435 }
1436
Amith Yamasani742a6712011-05-04 14:49:28 -07001437 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1438 int callingUid = Binder.getCallingUid();
1439 int packageUid;
1440 try {
1441 packageUid = getUidForPackage(packageName);
1442 } catch (PackageManager.NameNotFoundException ex) {
1443 throw new IllegalArgumentException("packageName and uid don't match packageName="
1444 + packageName);
1445 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001446 if (!UserHandle.isSameApp(callingUid, packageUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001447 throw new IllegalArgumentException("packageName and uid don't match packageName="
1448 + packageName);
1449 }
1450 return callingUid;
1451 }
1452
1453 void sendInitialBroadcasts() {
1454 synchronized (mAppWidgetIds) {
1455 ensureStateLoadedLocked();
1456 final int N = mInstalledProviders.size();
1457 for (int i = 0; i < N; i++) {
1458 Provider p = mInstalledProviders.get(i);
1459 if (p.instances.size() > 0) {
1460 sendEnableIntentLocked(p);
1461 int[] appWidgetIds = getAppWidgetIds(p);
1462 sendUpdateIntentLocked(p, appWidgetIds);
1463 registerForBroadcastsLocked(p, appWidgetIds);
1464 }
1465 }
1466 }
1467 }
1468
1469 // only call from initialization -- it assumes that the data structures are all empty
1470 void loadStateLocked() {
1471 AtomicFile file = savedStateFile();
1472 try {
1473 FileInputStream stream = file.openRead();
1474 readStateFromFileLocked(stream);
1475
1476 if (stream != null) {
1477 try {
1478 stream.close();
1479 } catch (IOException e) {
1480 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1481 }
1482 }
1483 } catch (FileNotFoundException e) {
1484 Slog.w(TAG, "Failed to read state: " + e);
1485 }
1486 }
1487
1488 void saveStateLocked() {
1489 AtomicFile file = savedStateFile();
1490 FileOutputStream stream;
1491 try {
1492 stream = file.startWrite();
1493 if (writeStateToFileLocked(stream)) {
1494 file.finishWrite(stream);
1495 } else {
1496 file.failWrite(stream);
1497 Slog.w(TAG, "Failed to save state, restoring backup.");
1498 }
1499 } catch (IOException e) {
1500 Slog.w(TAG, "Failed open state file for write: " + e);
1501 }
1502 }
1503
1504 boolean writeStateToFileLocked(FileOutputStream stream) {
1505 int N;
1506
1507 try {
1508 XmlSerializer out = new FastXmlSerializer();
1509 out.setOutput(stream, "utf-8");
1510 out.startDocument(null, true);
1511 out.startTag(null, "gs");
1512
1513 int providerIndex = 0;
1514 N = mInstalledProviders.size();
1515 for (int i = 0; i < N; i++) {
1516 Provider p = mInstalledProviders.get(i);
1517 if (p.instances.size() > 0) {
1518 out.startTag(null, "p");
1519 out.attribute(null, "pkg", p.info.provider.getPackageName());
1520 out.attribute(null, "cl", p.info.provider.getClassName());
1521 out.endTag(null, "p");
1522 p.tag = providerIndex;
1523 providerIndex++;
1524 }
1525 }
1526
1527 N = mHosts.size();
1528 for (int i = 0; i < N; i++) {
1529 Host host = mHosts.get(i);
1530 out.startTag(null, "h");
1531 out.attribute(null, "pkg", host.packageName);
1532 out.attribute(null, "id", Integer.toHexString(host.hostId));
1533 out.endTag(null, "h");
1534 host.tag = i;
1535 }
1536
1537 N = mAppWidgetIds.size();
1538 for (int i = 0; i < N; i++) {
1539 AppWidgetId id = mAppWidgetIds.get(i);
1540 out.startTag(null, "g");
1541 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1542 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1543 if (id.provider != null) {
1544 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1545 }
Adam Cohen0aa2d422012-09-07 17:37:26 -07001546 if (id.options != null) {
1547 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1548 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1549 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1550 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1551 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1552 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1553 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1554 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1555 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1556 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1557 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001558 out.endTag(null, "g");
1559 }
1560
Michael Jurka61a5b012012-04-13 10:39:45 -07001561 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1562 while (it.hasNext()) {
1563 out.startTag(null, "b");
1564 out.attribute(null, "packageName", it.next());
1565 out.endTag(null, "b");
1566 }
1567
Amith Yamasani742a6712011-05-04 14:49:28 -07001568 out.endTag(null, "gs");
1569
1570 out.endDocument();
1571 return true;
1572 } catch (IOException e) {
1573 Slog.w(TAG, "Failed to write state: " + e);
1574 return false;
1575 }
1576 }
1577
Adam Cohen0aa2d422012-09-07 17:37:26 -07001578 @SuppressWarnings("unused")
Amith Yamasani742a6712011-05-04 14:49:28 -07001579 void readStateFromFileLocked(FileInputStream stream) {
1580 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001581 try {
1582 XmlPullParser parser = Xml.newPullParser();
1583 parser.setInput(stream, null);
1584
1585 int type;
1586 int providerIndex = 0;
1587 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1588 do {
1589 type = parser.next();
1590 if (type == XmlPullParser.START_TAG) {
1591 String tag = parser.getName();
1592 if ("p".equals(tag)) {
1593 // TODO: do we need to check that this package has the same signature
1594 // as before?
1595 String pkg = parser.getAttributeValue(null, "pkg");
1596 String cl = parser.getAttributeValue(null, "cl");
1597
Amith Yamasanif203aee2012-08-29 18:41:53 -07001598 final IPackageManager packageManager = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -07001599 try {
Amith Yamasanif203aee2012-08-29 18:41:53 -07001600 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0,
1601 UserHandle.getCallingUserId());
1602 } catch (RemoteException e) {
1603 String[] pkgs = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001604 .currentToCanonicalPackageNames(new String[] { pkg });
1605 pkg = pkgs[0];
1606 }
1607
1608 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1609 if (p == null && mSafeMode) {
1610 // if we're in safe mode, make a temporary one
1611 p = new Provider();
1612 p.info = new AppWidgetProviderInfo();
1613 p.info.provider = new ComponentName(pkg, cl);
1614 p.zombie = true;
1615 mInstalledProviders.add(p);
1616 }
1617 if (p != null) {
1618 // if it wasn't uninstalled or something
1619 loadedProviders.put(providerIndex, p);
1620 }
1621 providerIndex++;
1622 } else if ("h".equals(tag)) {
1623 Host host = new Host();
1624
1625 // TODO: do we need to check that this package has the same signature
1626 // as before?
1627 host.packageName = parser.getAttributeValue(null, "pkg");
1628 try {
1629 host.uid = getUidForPackage(host.packageName);
1630 } catch (PackageManager.NameNotFoundException ex) {
1631 host.zombie = true;
1632 }
1633 if (!host.zombie || mSafeMode) {
1634 // In safe mode, we don't discard the hosts we don't recognize
1635 // so that they're not pruned from our list. Otherwise, we do.
1636 host.hostId = Integer
1637 .parseInt(parser.getAttributeValue(null, "id"), 16);
1638 mHosts.add(host);
1639 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001640 } else if ("b".equals(tag)) {
1641 String packageName = parser.getAttributeValue(null, "packageName");
1642 if (packageName != null) {
1643 mPackagesWithBindWidgetPermission.add(packageName);
1644 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001645 } else if ("g".equals(tag)) {
1646 AppWidgetId id = new AppWidgetId();
1647 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1648 if (id.appWidgetId >= mNextAppWidgetId) {
1649 mNextAppWidgetId = id.appWidgetId + 1;
1650 }
1651
Adam Cohen0aa2d422012-09-07 17:37:26 -07001652 Bundle options = new Bundle();
1653 String minWidthString = parser.getAttributeValue(null, "min_width");
1654 if (minWidthString != null) {
1655 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1656 Integer.parseInt(minWidthString, 16));
1657 }
1658 String minHeightString = parser.getAttributeValue(null, "min_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001659 if (minHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001660 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1661 Integer.parseInt(minHeightString, 16));
1662 }
Adam Cohendb38d8a2012-09-21 18:14:58 -07001663 String maxWidthString = parser.getAttributeValue(null, "max_width");
1664 if (maxWidthString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001665 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1666 Integer.parseInt(maxWidthString, 16));
1667 }
1668 String maxHeightString = parser.getAttributeValue(null, "max_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001669 if (maxHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001670 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1671 Integer.parseInt(maxHeightString, 16));
1672 }
1673 String categoryString = parser.getAttributeValue(null, "host_category");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001674 if (categoryString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001675 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1676 Integer.parseInt(categoryString, 16));
1677 }
1678 id.options = options;
1679
Amith Yamasani742a6712011-05-04 14:49:28 -07001680 String providerString = parser.getAttributeValue(null, "p");
1681 if (providerString != null) {
1682 // there's no provider if it hasn't been bound yet.
1683 // maybe we don't have to save this, but it brings the system
1684 // to the state it was in.
1685 int pIndex = Integer.parseInt(providerString, 16);
1686 id.provider = loadedProviders.get(pIndex);
1687 if (false) {
1688 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1689 + pIndex + " which is " + id.provider);
1690 }
1691 if (id.provider == null) {
1692 // This provider is gone. We just let the host figure out
1693 // that this happened when it fails to load it.
1694 continue;
1695 }
1696 }
1697
1698 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1699 id.host = mHosts.get(hIndex);
1700 if (id.host == null) {
1701 // This host is gone.
1702 continue;
1703 }
1704
1705 if (id.provider != null) {
1706 id.provider.instances.add(id);
1707 }
1708 id.host.instances.add(id);
1709 mAppWidgetIds.add(id);
1710 }
1711 }
1712 } while (type != XmlPullParser.END_DOCUMENT);
1713 success = true;
1714 } catch (NullPointerException e) {
1715 Slog.w(TAG, "failed parsing " + e);
1716 } catch (NumberFormatException e) {
1717 Slog.w(TAG, "failed parsing " + e);
1718 } catch (XmlPullParserException e) {
1719 Slog.w(TAG, "failed parsing " + e);
1720 } catch (IOException e) {
1721 Slog.w(TAG, "failed parsing " + e);
1722 } catch (IndexOutOfBoundsException e) {
1723 Slog.w(TAG, "failed parsing " + e);
1724 }
1725
1726 if (success) {
1727 // delete any hosts that didn't manage to get connected (should happen)
1728 // if it matters, they'll be reconnected.
1729 for (int i = mHosts.size() - 1; i >= 0; i--) {
1730 pruneHostLocked(mHosts.get(i));
1731 }
1732 } else {
1733 // failed reading, clean up
1734 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1735
1736 mAppWidgetIds.clear();
1737 mHosts.clear();
1738 final int N = mInstalledProviders.size();
1739 for (int i = 0; i < N; i++) {
1740 mInstalledProviders.get(i).instances.clear();
1741 }
1742 }
1743 }
1744
Amith Yamasani13593602012-03-22 16:16:17 -07001745 static File getSettingsFile(int userId) {
Amith Yamasani61f57372012-08-31 12:12:28 -07001746 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
Amith Yamasani13593602012-03-22 16:16:17 -07001747 }
1748
Amith Yamasani742a6712011-05-04 14:49:28 -07001749 AtomicFile savedStateFile() {
Amith Yamasani61f57372012-08-31 12:12:28 -07001750 File dir = Environment.getUserSystemDirectory(mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001751 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001752 if (!settingsFile.exists() && mUserId == 0) {
1753 if (!dir.exists()) {
1754 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001755 }
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001756 // Migrate old data
1757 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1758 // Method doesn't throw an exception on failure. Ignore any errors
1759 // in moving the file (like non-existence)
1760 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001761 }
1762 return new AtomicFile(settingsFile);
1763 }
1764
Amith Yamasani13593602012-03-22 16:16:17 -07001765 void onUserRemoved() {
1766 // prune the ones we don't want to keep
1767 int N = mInstalledProviders.size();
1768 for (int i = N - 1; i >= 0; i--) {
1769 Provider p = mInstalledProviders.get(i);
1770 cancelBroadcasts(p);
1771 }
1772 getSettingsFile(mUserId).delete();
1773 }
1774
Winson Chung7fbd2842012-06-13 10:35:51 -07001775 boolean addProvidersForPackageLocked(String pkgName) {
1776 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001777 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1778 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001779 List<ResolveInfo> broadcastReceivers;
1780 try {
1781 broadcastReceivers = mPm.queryIntentReceivers(intent,
1782 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1783 PackageManager.GET_META_DATA, mUserId);
1784 } catch (RemoteException re) {
1785 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001786 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001787 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001788 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1789 for (int i = 0; i < N; i++) {
1790 ResolveInfo ri = broadcastReceivers.get(i);
1791 ActivityInfo ai = ri.activityInfo;
1792 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1793 continue;
1794 }
1795 if (pkgName.equals(ai.packageName)) {
1796 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001797 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001798 }
1799 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001800
1801 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001802 }
1803
Winson Chunga3195052012-06-25 10:02:10 -07001804 /**
1805 * Updates all providers with the specified package names, and records any providers that were
1806 * pruned.
1807 *
1808 * @return whether any providers were updated
1809 */
1810 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001811 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001812 HashSet<String> keep = new HashSet<String>();
1813 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1814 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001815 List<ResolveInfo> broadcastReceivers;
1816 try {
1817 broadcastReceivers = mPm.queryIntentReceivers(intent,
1818 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1819 PackageManager.GET_META_DATA, mUserId);
1820 } catch (RemoteException re) {
1821 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001822 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001823 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001824
1825 // add the missing ones and collect which ones to keep
1826 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1827 for (int i = 0; i < N; i++) {
1828 ResolveInfo ri = broadcastReceivers.get(i);
1829 ActivityInfo ai = ri.activityInfo;
1830 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1831 continue;
1832 }
1833 if (pkgName.equals(ai.packageName)) {
1834 ComponentName component = new ComponentName(ai.packageName, ai.name);
1835 Provider p = lookupProviderLocked(component);
1836 if (p == null) {
1837 if (addProviderLocked(ri)) {
1838 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001839 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001840 }
1841 } else {
1842 Provider parsed = parseProviderInfoXml(component, ri);
1843 if (parsed != null) {
1844 keep.add(ai.name);
1845 // Use the new AppWidgetProviderInfo.
1846 p.info = parsed.info;
1847 // If it's enabled
1848 final int M = p.instances.size();
1849 if (M > 0) {
1850 int[] appWidgetIds = getAppWidgetIds(p);
1851 // Reschedule for the new updatePeriodMillis (don't worry about handling
1852 // it specially if updatePeriodMillis didn't change because we just sent
1853 // an update, and the next one will be updatePeriodMillis from now).
1854 cancelBroadcasts(p);
1855 registerForBroadcastsLocked(p, appWidgetIds);
1856 // If it's currently showing, call back with the new
1857 // AppWidgetProviderInfo.
1858 for (int j = 0; j < M; j++) {
1859 AppWidgetId id = p.instances.get(j);
1860 id.views = null;
1861 if (id.host != null && id.host.callbacks != null) {
1862 try {
1863 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1864 } catch (RemoteException ex) {
1865 // It failed; remove the callback. No need to prune because
1866 // we know that this host is still referenced by this
1867 // instance.
1868 id.host.callbacks = null;
1869 }
1870 }
1871 }
1872 // Now that we've told the host, push out an update.
1873 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07001874 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001875 }
1876 }
1877 }
1878 }
1879 }
1880
1881 // prune the ones we don't want to keep
1882 N = mInstalledProviders.size();
1883 for (int i = N - 1; i >= 0; i--) {
1884 Provider p = mInstalledProviders.get(i);
1885 if (pkgName.equals(p.info.provider.getPackageName())
1886 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07001887 if (removedProviders != null) {
1888 removedProviders.add(p.info.provider);
1889 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001890 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001891 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001892 }
1893 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001894
1895 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07001896 }
1897
Winson Chung7fbd2842012-06-13 10:35:51 -07001898 boolean removeProvidersForPackageLocked(String pkgName) {
1899 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001900 int N = mInstalledProviders.size();
1901 for (int i = N - 1; i >= 0; i--) {
1902 Provider p = mInstalledProviders.get(i);
1903 if (pkgName.equals(p.info.provider.getPackageName())) {
1904 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001905 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001906 }
1907 }
1908
1909 // Delete the hosts for this package too
1910 //
1911 // By now, we have removed any AppWidgets that were in any hosts here,
1912 // so we don't need to worry about sending DISABLE broadcasts to them.
1913 N = mHosts.size();
1914 for (int i = N - 1; i >= 0; i--) {
1915 Host host = mHosts.get(i);
1916 if (pkgName.equals(host.packageName)) {
1917 deleteHostLocked(host);
1918 }
1919 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001920
1921 return providersRemoved;
1922 }
1923
1924 void notifyHostsForProvidersChangedLocked() {
1925 final int N = mHosts.size();
1926 for (int i = N - 1; i >= 0; i--) {
1927 Host host = mHosts.get(i);
1928 try {
1929 if (host.callbacks != null) {
1930 host.callbacks.providersChanged();
1931 }
1932 } catch (RemoteException ex) {
1933 // It failed; remove the callback. No need to prune because
1934 // we know that this host is still referenced by this
1935 // instance.
1936 host.callbacks = null;
1937 }
1938 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001939 }
1940}