blob: 6a313a00dc0a83714ba0a8d0bbf38679cbf444b2 [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 Millerf229e4d32012-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
Amith Yamasani8320de82012-10-05 16:10:38 -070090 private static boolean DBG = false;
91
Amith Yamasani742a6712011-05-04 14:49:28 -070092 /*
93 * When identifying a Host or Provider based on the calling process, use the uid field. When
94 * identifying a Host or Provider based on a package manager broadcast, use the package given.
95 */
96
97 static class Provider {
98 int uid;
99 AppWidgetProviderInfo info;
100 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
101 PendingIntent broadcast;
102 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
103
104 int tag; // for use while saving state (the index)
105 }
106
107 static class Host {
108 int uid;
109 int hostId;
110 String packageName;
111 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
112 IAppWidgetHost callbacks;
113 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
114
115 int tag; // for use while saving state (the index)
116 }
117
118 static class AppWidgetId {
119 int appWidgetId;
120 Provider provider;
121 RemoteViews views;
Adam Cohend2097eb2012-05-01 18:10:28 -0700122 Bundle options;
Amith Yamasani742a6712011-05-04 14:49:28 -0700123 Host host;
124 }
125
126 /**
127 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
128 * needs to be a static inner class since a reference to the ServiceConnection is held globally
129 * and may lead us to leak AppWidgetService instances (if there were more than one).
130 */
131 static class ServiceConnectionProxy implements ServiceConnection {
132 private final IBinder mConnectionCb;
133
134 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
135 mConnectionCb = connectionCb;
136 }
137
138 public void onServiceConnected(ComponentName name, IBinder service) {
139 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
140 .asInterface(mConnectionCb);
141 try {
142 cb.onServiceConnected(service);
143 } catch (Exception e) {
144 e.printStackTrace();
145 }
146 }
147
148 public void onServiceDisconnected(ComponentName name) {
149 disconnect();
150 }
151
152 public void disconnect() {
153 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
154 .asInterface(mConnectionCb);
155 try {
156 cb.onServiceDisconnected();
157 } catch (Exception e) {
158 e.printStackTrace();
159 }
160 }
161 }
162
163 // Manages active connections to RemoteViewsServices
164 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
165 // Manages persistent references to RemoteViewsServices from different App Widgets
166 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
167
168 Context mContext;
169 Locale mLocale;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700170 IPackageManager mPm;
Amith Yamasani742a6712011-05-04 14:49:28 -0700171 AlarmManager mAlarmManager;
172 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
173 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
174 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
175 ArrayList<Host> mHosts = new ArrayList<Host>();
Michael Jurka61a5b012012-04-13 10:39:45 -0700176 // set of package names
177 HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
Amith Yamasani742a6712011-05-04 14:49:28 -0700178 boolean mSafeMode;
179 int mUserId;
180 boolean mStateLoaded;
Adam Cohen311c79c2012-05-10 14:44:38 -0700181 int mMaxWidgetBitmapMemory;
Amith Yamasani742a6712011-05-04 14:49:28 -0700182
183 // These are for debugging only -- widgets are going missing in some rare instances
184 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
185 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
186
187 AppWidgetServiceImpl(Context context, int userId) {
188 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700189 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700190 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
191 mUserId = userId;
Adam Cohen311c79c2012-05-10 14:44:38 -0700192 computeMaximumWidgetBitmapMemory();
193 }
194
195 void computeMaximumWidgetBitmapMemory() {
196 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Jeff Browna8b9def2012-07-23 14:22:49 -0700197 Display display = wm.getDefaultDisplay();
198 Point size = new Point();
199 display.getRealSize(size);
Winson Chunge92aad42012-06-22 14:11:47 -0700200 // Cap memory usage at 1.5 times the size of the display
201 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
Jeff Browna8b9def2012-07-23 14:22:49 -0700202 mMaxWidgetBitmapMemory = 6 * size.x * size.y;
Amith Yamasani742a6712011-05-04 14:49:28 -0700203 }
204
205 public void systemReady(boolean safeMode) {
206 mSafeMode = safeMode;
207
208 synchronized (mAppWidgetIds) {
209 ensureStateLoadedLocked();
210 }
211 }
212
Amith Yamasani8320de82012-10-05 16:10:38 -0700213 private void log(String msg) {
214 Slog.i(TAG, "u=" + mUserId + ": " + msg);
215 }
216
Amith Yamasani742a6712011-05-04 14:49:28 -0700217 void onConfigurationChanged() {
Amith Yamasani8320de82012-10-05 16:10:38 -0700218 if (DBG) log("Got onConfigurationChanged()");
Amith Yamasani742a6712011-05-04 14:49:28 -0700219 Locale revised = Locale.getDefault();
220 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
221 mLocale = revised;
222
223 synchronized (mAppWidgetIds) {
224 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700225 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
226 // list of installed providers and skip providers that we don't need to update.
227 // Also note that remove the provider does not clear the Provider component data.
228 ArrayList<Provider> installedProviders =
229 new ArrayList<Provider>(mInstalledProviders);
230 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
231 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700232 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700233 Provider p = installedProviders.get(i);
234 ComponentName cn = p.info.provider;
235 if (!removedProviders.contains(cn)) {
236 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
237 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700238 }
239 saveStateLocked();
240 }
241 }
242 }
243
244 void onBroadcastReceived(Intent intent) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700245 if (DBG) log("onBroadcast " + intent);
Amith Yamasani742a6712011-05-04 14:49:28 -0700246 final String action = intent.getAction();
247 boolean added = false;
248 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700249 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700250 String pkgList[] = null;
251 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
252 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
253 added = true;
254 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
255 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
256 added = false;
257 } else {
258 Uri uri = intent.getData();
259 if (uri == null) {
260 return;
261 }
262 String pkgName = uri.getSchemeSpecificPart();
263 if (pkgName == null) {
264 return;
265 }
266 pkgList = new String[] { pkgName };
267 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
268 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
269 }
270 if (pkgList == null || pkgList.length == 0) {
271 return;
272 }
273 if (added || changed) {
274 synchronized (mAppWidgetIds) {
275 ensureStateLoadedLocked();
276 Bundle extras = intent.getExtras();
277 if (changed
278 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
279 for (String pkgName : pkgList) {
280 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700281 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700282 }
283 } else {
284 // The package was just added
285 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700286 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700287 }
288 }
289 saveStateLocked();
290 }
291 } else {
292 Bundle extras = intent.getExtras();
293 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
294 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
295 } else {
296 synchronized (mAppWidgetIds) {
297 ensureStateLoadedLocked();
298 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700299 providersModified |= removeProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700300 saveStateLocked();
301 }
302 }
303 }
304 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700305
306 if (providersModified) {
307 // If the set of providers has been modified, notify each active AppWidgetHost
308 synchronized (mAppWidgetIds) {
309 ensureStateLoadedLocked();
310 notifyHostsForProvidersChangedLocked();
311 }
312 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700313 }
314
315 private void dumpProvider(Provider p, int index, PrintWriter pw) {
316 AppWidgetProviderInfo info = p.info;
317 pw.print(" ["); pw.print(index); pw.print("] provider ");
318 pw.print(info.provider.flattenToShortString());
319 pw.println(':');
320 pw.print(" min=("); pw.print(info.minWidth);
321 pw.print("x"); pw.print(info.minHeight);
322 pw.print(") minResize=("); pw.print(info.minResizeWidth);
323 pw.print("x"); pw.print(info.minResizeHeight);
324 pw.print(") updatePeriodMillis=");
325 pw.print(info.updatePeriodMillis);
326 pw.print(" resizeMode=");
327 pw.print(info.resizeMode);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700328 pw.print(info.widgetCategory);
329 pw.print(info.widgetFeatures);
Amith Yamasani742a6712011-05-04 14:49:28 -0700330 pw.print(" autoAdvanceViewId=");
331 pw.print(info.autoAdvanceViewId);
332 pw.print(" initialLayout=#");
333 pw.print(Integer.toHexString(info.initialLayout));
334 pw.print(" zombie="); pw.println(p.zombie);
335 }
336
337 private void dumpHost(Host host, int index, PrintWriter pw) {
338 pw.print(" ["); pw.print(index); pw.print("] hostId=");
339 pw.print(host.hostId); pw.print(' ');
340 pw.print(host.packageName); pw.print('/');
341 pw.print(host.uid); pw.println(':');
342 pw.print(" callbacks="); pw.println(host.callbacks);
343 pw.print(" instances.size="); pw.print(host.instances.size());
344 pw.print(" zombie="); pw.println(host.zombie);
345 }
346
347 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
348 pw.print(" ["); pw.print(index); pw.print("] id=");
349 pw.println(id.appWidgetId);
350 pw.print(" hostId=");
351 pw.print(id.host.hostId); pw.print(' ');
352 pw.print(id.host.packageName); pw.print('/');
353 pw.println(id.host.uid);
354 if (id.provider != null) {
355 pw.print(" provider=");
356 pw.println(id.provider.info.provider.flattenToShortString());
357 }
358 if (id.host != null) {
359 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
360 }
361 if (id.views != null) {
362 pw.print(" views="); pw.println(id.views);
363 }
364 }
365
366 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
367 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
368 != PackageManager.PERMISSION_GRANTED) {
369 pw.println("Permission Denial: can't dump from from pid="
370 + Binder.getCallingPid()
371 + ", uid=" + Binder.getCallingUid());
372 return;
373 }
374
375 synchronized (mAppWidgetIds) {
376 int N = mInstalledProviders.size();
377 pw.println("Providers:");
378 for (int i=0; i<N; i++) {
379 dumpProvider(mInstalledProviders.get(i), i, pw);
380 }
381
382 N = mAppWidgetIds.size();
383 pw.println(" ");
384 pw.println("AppWidgetIds:");
385 for (int i=0; i<N; i++) {
386 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
387 }
388
389 N = mHosts.size();
390 pw.println(" ");
391 pw.println("Hosts:");
392 for (int i=0; i<N; i++) {
393 dumpHost(mHosts.get(i), i, pw);
394 }
395
396 N = mDeletedProviders.size();
397 pw.println(" ");
398 pw.println("Deleted Providers:");
399 for (int i=0; i<N; i++) {
400 dumpProvider(mDeletedProviders.get(i), i, pw);
401 }
402
403 N = mDeletedHosts.size();
404 pw.println(" ");
405 pw.println("Deleted Hosts:");
406 for (int i=0; i<N; i++) {
407 dumpHost(mDeletedHosts.get(i), i, pw);
408 }
409 }
410 }
411
412 private void ensureStateLoadedLocked() {
413 if (!mStateLoaded) {
414 loadAppWidgetList();
415 loadStateLocked();
416 mStateLoaded = true;
417 }
418 }
419
420 public int allocateAppWidgetId(String packageName, int hostId) {
Jim Millerf229e4d32012-09-12 20:32:50 -0700421 int callingUid = enforceSystemOrCallingUid(packageName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700422 synchronized (mAppWidgetIds) {
423 ensureStateLoadedLocked();
424 int appWidgetId = mNextAppWidgetId++;
425
426 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
427
428 AppWidgetId id = new AppWidgetId();
429 id.appWidgetId = appWidgetId;
430 id.host = host;
431
432 host.instances.add(id);
433 mAppWidgetIds.add(id);
434
435 saveStateLocked();
Amith Yamasani8320de82012-10-05 16:10:38 -0700436 if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
437 + " id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700438 return appWidgetId;
439 }
440 }
441
442 public void deleteAppWidgetId(int appWidgetId) {
443 synchronized (mAppWidgetIds) {
444 ensureStateLoadedLocked();
445 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
446 if (id != null) {
447 deleteAppWidgetLocked(id);
448 saveStateLocked();
449 }
450 }
451 }
452
453 public void deleteHost(int hostId) {
454 synchronized (mAppWidgetIds) {
455 ensureStateLoadedLocked();
456 int callingUid = Binder.getCallingUid();
457 Host host = lookupHostLocked(callingUid, hostId);
458 if (host != null) {
459 deleteHostLocked(host);
460 saveStateLocked();
461 }
462 }
463 }
464
465 public void deleteAllHosts() {
466 synchronized (mAppWidgetIds) {
467 ensureStateLoadedLocked();
468 int callingUid = Binder.getCallingUid();
469 final int N = mHosts.size();
470 boolean changed = false;
471 for (int i = N - 1; i >= 0; i--) {
472 Host host = mHosts.get(i);
473 if (host.uid == callingUid) {
474 deleteHostLocked(host);
475 changed = true;
476 }
477 }
478 if (changed) {
479 saveStateLocked();
480 }
481 }
482 }
483
484 void deleteHostLocked(Host host) {
485 final int N = host.instances.size();
486 for (int i = N - 1; i >= 0; i--) {
487 AppWidgetId id = host.instances.get(i);
488 deleteAppWidgetLocked(id);
489 }
490 host.instances.clear();
491 mHosts.remove(host);
492 mDeletedHosts.add(host);
493 // it's gone or going away, abruptly drop the callback connection
494 host.callbacks = null;
495 }
496
497 void deleteAppWidgetLocked(AppWidgetId id) {
498 // We first unbind all services that are bound to this id
499 unbindAppWidgetRemoteViewsServicesLocked(id);
500
501 Host host = id.host;
502 host.instances.remove(id);
503 pruneHostLocked(host);
504
505 mAppWidgetIds.remove(id);
506
507 Provider p = id.provider;
508 if (p != null) {
509 p.instances.remove(id);
510 if (!p.zombie) {
511 // send the broacast saying that this appWidgetId has been deleted
512 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
513 intent.setComponent(p.info.provider);
514 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700515 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700516 if (p.instances.size() == 0) {
517 // cancel the future updates
518 cancelBroadcasts(p);
519
520 // send the broacast saying that the provider is not in use any more
521 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
522 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700523 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700524 }
525 }
526 }
527 }
528
529 void cancelBroadcasts(Provider p) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700530 if (DBG) log("cancelBroadcasts for " + p);
Amith Yamasani742a6712011-05-04 14:49:28 -0700531 if (p.broadcast != null) {
532 mAlarmManager.cancel(p.broadcast);
533 long token = Binder.clearCallingIdentity();
534 try {
535 p.broadcast.cancel();
536 } finally {
537 Binder.restoreCallingIdentity(token);
538 }
539 p.broadcast = null;
540 }
541 }
542
Adam Cohen0aa2d422012-09-07 17:37:26 -0700543 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700544 if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
545 + " provider=" + provider);
Amith Yamasani742a6712011-05-04 14:49:28 -0700546 final long ident = Binder.clearCallingIdentity();
547 try {
548 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700549 options = cloneIfLocalBinder(options);
Amith Yamasani742a6712011-05-04 14:49:28 -0700550 ensureStateLoadedLocked();
551 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
552 if (id == null) {
553 throw new IllegalArgumentException("bad appWidgetId");
554 }
555 if (id.provider != null) {
556 throw new IllegalArgumentException("appWidgetId " + appWidgetId
557 + " already bound to " + id.provider.info.provider);
558 }
559 Provider p = lookupProviderLocked(provider);
560 if (p == null) {
561 throw new IllegalArgumentException("not a appwidget provider: " + provider);
562 }
563 if (p.zombie) {
564 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
565 + " safe mode: " + provider);
566 }
567
Amith Yamasani742a6712011-05-04 14:49:28 -0700568 id.provider = p;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700569 if (options == null) {
570 options = new Bundle();
571 }
572 id.options = options;
573
574 // We need to provide a default value for the widget category if it is not specified
575 if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
576 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
577 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
578 }
579
Amith Yamasani742a6712011-05-04 14:49:28 -0700580 p.instances.add(id);
581 int instancesSize = p.instances.size();
582 if (instancesSize == 1) {
583 // tell the provider that it's ready
584 sendEnableIntentLocked(p);
585 }
586
587 // send an update now -- We need this update now, and just for this appWidgetId.
588 // It's less critical when the next one happens, so when we schedule the next one,
589 // we add updatePeriodMillis to its start time. That time will have some slop,
590 // but that's okay.
591 sendUpdateIntentLocked(p, new int[] { appWidgetId });
592
593 // schedule the future updates
594 registerForBroadcastsLocked(p, getAppWidgetIds(p));
595 saveStateLocked();
596 }
597 } finally {
598 Binder.restoreCallingIdentity(ident);
599 }
600 }
601
Adam Cohen0aa2d422012-09-07 17:37:26 -0700602 public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700603 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
604 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700605 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700606 }
607
608 public boolean bindAppWidgetIdIfAllowed(
Adam Cohen0aa2d422012-09-07 17:37:26 -0700609 String packageName, int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700610 try {
611 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
612 } catch (SecurityException se) {
613 if (!callerHasBindAppWidgetPermission(packageName)) {
614 return false;
615 }
616 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700617 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700618 return true;
619 }
620
621 private boolean callerHasBindAppWidgetPermission(String packageName) {
622 int callingUid = Binder.getCallingUid();
623 try {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700624 if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700625 return false;
626 }
627 } catch (Exception e) {
628 return false;
629 }
630 synchronized (mAppWidgetIds) {
631 ensureStateLoadedLocked();
632 return mPackagesWithBindWidgetPermission.contains(packageName);
633 }
634 }
635
636 public boolean hasBindAppWidgetPermission(String packageName) {
637 mContext.enforceCallingPermission(
638 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
639 "hasBindAppWidgetPermission packageName=" + packageName);
640
641 synchronized (mAppWidgetIds) {
642 ensureStateLoadedLocked();
643 return mPackagesWithBindWidgetPermission.contains(packageName);
644 }
645 }
646
647 public void setBindAppWidgetPermission(String packageName, boolean permission) {
648 mContext.enforceCallingPermission(
649 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
650 "setBindAppWidgetPermission packageName=" + packageName);
651
652 synchronized (mAppWidgetIds) {
653 ensureStateLoadedLocked();
654 if (permission) {
655 mPackagesWithBindWidgetPermission.add(packageName);
656 } else {
657 mPackagesWithBindWidgetPermission.remove(packageName);
658 }
659 }
660 saveStateLocked();
661 }
662
Amith Yamasani742a6712011-05-04 14:49:28 -0700663 // Binds to a specific RemoteViewsService
664 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
665 synchronized (mAppWidgetIds) {
666 ensureStateLoadedLocked();
667 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
668 if (id == null) {
669 throw new IllegalArgumentException("bad appWidgetId");
670 }
671 final ComponentName componentName = intent.getComponent();
672 try {
Amith Yamasani98edc952012-09-25 14:09:27 -0700673 final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
674 PackageManager.GET_PERMISSIONS, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700675 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
676 throw new SecurityException("Selected service does not require "
677 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
678 }
Amith Yamasani98edc952012-09-25 14:09:27 -0700679 } catch (RemoteException e) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700680 throw new IllegalArgumentException("Unknown component " + componentName);
681 }
682
683 // If there is already a connection made for this service intent, then disconnect from
684 // that first. (This does not allow multiple connections to the same service under
685 // the same key)
686 ServiceConnectionProxy conn = null;
687 FilterComparison fc = new FilterComparison(intent);
688 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
689 if (mBoundRemoteViewsServices.containsKey(key)) {
690 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
691 conn.disconnect();
692 mContext.unbindService(conn);
693 mBoundRemoteViewsServices.remove(key);
694 }
695
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700696 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700697 // Bind to the RemoteViewsService (which will trigger a callback to the
698 // RemoteViewsAdapter.onServiceConnected())
699 final long token = Binder.clearCallingIdentity();
700 try {
701 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800702 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700703 mBoundRemoteViewsServices.put(key, conn);
704 } finally {
705 Binder.restoreCallingIdentity(token);
706 }
707
708 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
709 // when we can call back to the RemoteViewsService later to destroy associated
710 // factories.
711 incrementAppWidgetServiceRefCount(appWidgetId, fc);
712 }
713 }
714
715 // Unbinds from a specific RemoteViewsService
716 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
717 synchronized (mAppWidgetIds) {
718 ensureStateLoadedLocked();
719 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
720 // RemoteViewsAdapter)
721 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
722 intent));
723 if (mBoundRemoteViewsServices.containsKey(key)) {
724 // We don't need to use the appWidgetId until after we are sure there is something
725 // to unbind. Note that this may mask certain issues with apps calling unbind()
726 // more than necessary.
727 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
728 if (id == null) {
729 throw new IllegalArgumentException("bad appWidgetId");
730 }
731
732 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
733 .get(key);
734 conn.disconnect();
735 mContext.unbindService(conn);
736 mBoundRemoteViewsServices.remove(key);
737 } else {
738 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
739 }
740 }
741 }
742
743 // Unbinds from a RemoteViewsService when we delete an app widget
744 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
745 int appWidgetId = id.appWidgetId;
746 // Unbind all connections to Services bound to this AppWidgetId
747 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
748 .iterator();
749 while (it.hasNext()) {
750 final Pair<Integer, Intent.FilterComparison> key = it.next();
751 if (key.first.intValue() == appWidgetId) {
752 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
753 .get(key);
754 conn.disconnect();
755 mContext.unbindService(conn);
756 it.remove();
757 }
758 }
759
760 // Check if we need to destroy any services (if no other app widgets are
761 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800762 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700763 }
764
765 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800766 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700767 final ServiceConnection conn = new ServiceConnection() {
768 @Override
769 public void onServiceConnected(ComponentName name, IBinder service) {
770 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
771 try {
772 cb.onDestroy(intent);
773 } catch (RemoteException e) {
774 e.printStackTrace();
775 } catch (RuntimeException e) {
776 e.printStackTrace();
777 }
778 mContext.unbindService(this);
779 }
780
781 @Override
782 public void onServiceDisconnected(android.content.ComponentName name) {
783 // Do nothing
784 }
785 };
786
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700787 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700788 // Bind to the service and remove the static intent->factory mapping in the
789 // RemoteViewsService.
790 final long token = Binder.clearCallingIdentity();
791 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800792 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700793 } finally {
794 Binder.restoreCallingIdentity(token);
795 }
796 }
797
798 // Adds to the ref-count for a given RemoteViewsService intent
799 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
800 HashSet<Integer> appWidgetIds = null;
801 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
802 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
803 } else {
804 appWidgetIds = new HashSet<Integer>();
805 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
806 }
807 appWidgetIds.add(appWidgetId);
808 }
809
810 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
811 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800812 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700813 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
814 while (it.hasNext()) {
815 final FilterComparison key = it.next();
816 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800817 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700818 // If we have removed the last app widget referencing this service, then we
819 // should destroy it and remove it from this set
820 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800821 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700822 it.remove();
823 }
824 }
825 }
826 }
827
828 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
829 synchronized (mAppWidgetIds) {
830 ensureStateLoadedLocked();
831 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
832 if (id != null && id.provider != null && !id.provider.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700833 return cloneIfLocalBinder(id.provider.info);
Amith Yamasani742a6712011-05-04 14:49:28 -0700834 }
835 return null;
836 }
837 }
838
839 public RemoteViews getAppWidgetViews(int appWidgetId) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700840 if (DBG) log("getAppWidgetViews id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700841 synchronized (mAppWidgetIds) {
842 ensureStateLoadedLocked();
843 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
844 if (id != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700845 return cloneIfLocalBinder(id.views);
Amith Yamasani742a6712011-05-04 14:49:28 -0700846 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700847 if (DBG) log(" couldn't find appwidgetid");
Amith Yamasani742a6712011-05-04 14:49:28 -0700848 return null;
849 }
850 }
851
852 public List<AppWidgetProviderInfo> getInstalledProviders() {
853 synchronized (mAppWidgetIds) {
854 ensureStateLoadedLocked();
855 final int N = mInstalledProviders.size();
856 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
857 for (int i = 0; i < N; i++) {
858 Provider p = mInstalledProviders.get(i);
859 if (!p.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700860 result.add(cloneIfLocalBinder(p.info));
Amith Yamasani742a6712011-05-04 14:49:28 -0700861 }
862 }
863 return result;
864 }
865 }
866
867 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
868 if (appWidgetIds == null) {
869 return;
870 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700871 if (DBG) log("updateAppWidgetIds views: " + views);
Adam Cohenf08a8b72012-07-16 12:02:10 -0700872 int bitmapMemoryUsage = 0;
873 if (views != null) {
874 bitmapMemoryUsage = views.estimateMemoryUsage();
875 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700876 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
877 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
878 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
879 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
880 " fill the device's screen once.");
881 }
882
Amith Yamasani742a6712011-05-04 14:49:28 -0700883 if (appWidgetIds.length == 0) {
884 return;
885 }
886 final int N = appWidgetIds.length;
887
888 synchronized (mAppWidgetIds) {
889 ensureStateLoadedLocked();
890 for (int i = 0; i < N; i++) {
891 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
892 updateAppWidgetInstanceLocked(id, views);
893 }
894 }
895 }
896
Adam Cohend2097eb2012-05-01 18:10:28 -0700897 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700898 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700899 options = cloneIfLocalBinder(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700900 ensureStateLoadedLocked();
901 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
902
903 if (id == null) {
904 return;
905 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700906
Adam Cohene8724c82012-04-19 17:11:40 -0700907 Provider p = id.provider;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700908 // Merge the options
909 id.options.putAll(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700910
911 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700912 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700913 intent.setComponent(p.info.provider);
914 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700915 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700916 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Adam Cohen0aa2d422012-09-07 17:37:26 -0700917 saveStateLocked();
Adam Cohene8724c82012-04-19 17:11:40 -0700918 }
919 }
920
Adam Cohend2097eb2012-05-01 18:10:28 -0700921 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -0700922 synchronized (mAppWidgetIds) {
923 ensureStateLoadedLocked();
924 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700925 if (id != null && id.options != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700926 return cloneIfLocalBinder(id.options);
Adam Cohene8724c82012-04-19 17:11:40 -0700927 } else {
928 return Bundle.EMPTY;
929 }
930 }
931 }
932
Amith Yamasani742a6712011-05-04 14:49:28 -0700933 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
934 if (appWidgetIds == null) {
935 return;
936 }
937 if (appWidgetIds.length == 0) {
938 return;
939 }
940 final int N = appWidgetIds.length;
941
942 synchronized (mAppWidgetIds) {
943 ensureStateLoadedLocked();
944 for (int i = 0; i < N; i++) {
945 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung66119882012-10-11 14:26:25 -0700946 if (id.views != null) {
947 // Only trigger a partial update for a widget if it has received a full update
948 updateAppWidgetInstanceLocked(id, views, true);
949 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700950 }
951 }
952 }
953
954 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
955 if (appWidgetIds == null) {
956 return;
957 }
958 if (appWidgetIds.length == 0) {
959 return;
960 }
961 final int N = appWidgetIds.length;
962
963 synchronized (mAppWidgetIds) {
964 ensureStateLoadedLocked();
965 for (int i = 0; i < N; i++) {
966 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
967 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
968 }
969 }
970 }
971
972 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
973 synchronized (mAppWidgetIds) {
974 ensureStateLoadedLocked();
975 Provider p = lookupProviderLocked(provider);
976 if (p == null) {
977 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
978 return;
979 }
980 ArrayList<AppWidgetId> instances = p.instances;
981 final int callingUid = Binder.getCallingUid();
982 final int N = instances.size();
983 for (int i = 0; i < N; i++) {
984 AppWidgetId id = instances.get(i);
985 if (canAccessAppWidgetId(id, callingUid)) {
986 updateAppWidgetInstanceLocked(id, views);
987 }
988 }
989 }
990 }
991
992 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
993 updateAppWidgetInstanceLocked(id, views, false);
994 }
995
996 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
997 // allow for stale appWidgetIds and other badness
998 // lookup also checks that the calling process can access the appWidgetId
999 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1000 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1001
Winson Chung66119882012-10-11 14:26:25 -07001002 if (!isPartialUpdate) {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001003 // For a full update we replace the RemoteViews completely.
Amith Yamasani742a6712011-05-04 14:49:28 -07001004 id.views = views;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001005 } else {
1006 // For a partial update, we merge the new RemoteViews with the old.
1007 id.views.mergeRemoteViews(views);
1008 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001009
1010 // is anyone listening?
1011 if (id.host.callbacks != null) {
1012 try {
1013 // the lock is held, but this is a oneway call
1014 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
1015 } catch (RemoteException e) {
1016 // It failed; remove the callback. No need to prune because
1017 // we know that this host is still referenced by this instance.
1018 id.host.callbacks = null;
1019 }
1020 }
1021 }
1022 }
1023
1024 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1025 // allow for stale appWidgetIds and other badness
1026 // lookup also checks that the calling process can access the appWidgetId
1027 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1028 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1029 // is anyone listening?
1030 if (id.host.callbacks != null) {
1031 try {
1032 // the lock is held, but this is a oneway call
1033 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1034 } catch (RemoteException e) {
1035 // It failed; remove the callback. No need to prune because
1036 // we know that this host is still referenced by this instance.
1037 id.host.callbacks = null;
1038 }
1039 }
1040
1041 // If the host is unavailable, then we call the associated
1042 // RemoteViewsFactory.onDataSetChanged() directly
1043 if (id.host.callbacks == null) {
1044 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1045 for (FilterComparison key : keys) {
1046 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1047 Intent intent = key.getIntent();
1048
1049 final ServiceConnection conn = new ServiceConnection() {
1050 @Override
1051 public void onServiceConnected(ComponentName name, IBinder service) {
1052 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1053 .asInterface(service);
1054 try {
1055 cb.onDataSetChangedAsync();
1056 } catch (RemoteException e) {
1057 e.printStackTrace();
1058 } catch (RuntimeException e) {
1059 e.printStackTrace();
1060 }
1061 mContext.unbindService(this);
1062 }
1063
1064 @Override
1065 public void onServiceDisconnected(android.content.ComponentName name) {
1066 // Do nothing
1067 }
1068 };
1069
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001070 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001071 // Bind to the service and call onDataSetChanged()
1072 final long token = Binder.clearCallingIdentity();
1073 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001074 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001075 } finally {
1076 Binder.restoreCallingIdentity(token);
1077 }
1078 }
1079 }
1080 }
1081 }
1082 }
1083
Adam Cohen3ff2d862012-09-26 14:07:57 -07001084 private boolean isLocalBinder() {
1085 return Process.myPid() == Binder.getCallingPid();
1086 }
1087
1088 private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1089 if (isLocalBinder() && rv != null) {
1090 return rv.clone();
1091 }
1092 return rv;
1093 }
1094
1095 private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
1096 if (isLocalBinder() && info != null) {
1097 return info.clone();
1098 }
1099 return info;
1100 }
1101
1102 private Bundle cloneIfLocalBinder(Bundle bundle) {
1103 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
1104 // if we start adding objects to the options. Further, it would only be an issue if keyguard
1105 // used such options.
1106 if (isLocalBinder() && bundle != null) {
1107 return (Bundle) bundle.clone();
1108 }
1109 return bundle;
1110 }
1111
Amith Yamasani742a6712011-05-04 14:49:28 -07001112 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1113 List<RemoteViews> updatedViews) {
1114 int callingUid = enforceCallingUid(packageName);
1115 synchronized (mAppWidgetIds) {
1116 ensureStateLoadedLocked();
1117 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1118 host.callbacks = callbacks;
1119
1120 updatedViews.clear();
1121
1122 ArrayList<AppWidgetId> instances = host.instances;
1123 int N = instances.size();
1124 int[] updatedIds = new int[N];
1125 for (int i = 0; i < N; i++) {
1126 AppWidgetId id = instances.get(i);
1127 updatedIds[i] = id.appWidgetId;
Adam Cohen3ff2d862012-09-26 14:07:57 -07001128 updatedViews.add(cloneIfLocalBinder(id.views));
Amith Yamasani742a6712011-05-04 14:49:28 -07001129 }
1130 return updatedIds;
1131 }
1132 }
1133
1134 public void stopListening(int hostId) {
1135 synchronized (mAppWidgetIds) {
1136 ensureStateLoadedLocked();
1137 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1138 if (host != null) {
1139 host.callbacks = null;
1140 pruneHostLocked(host);
1141 }
1142 }
1143 }
1144
1145 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1146 if (id.host.uid == callingUid) {
1147 // Apps hosting the AppWidget have access to it.
1148 return true;
1149 }
1150 if (id.provider != null && id.provider.uid == callingUid) {
1151 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1152 return true;
1153 }
1154 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1155 // Apps that can bind have access to all appWidgetIds.
1156 return true;
1157 }
1158 // Nobody else can access it.
1159 return false;
1160 }
1161
1162 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1163 int callingUid = Binder.getCallingUid();
1164 final int N = mAppWidgetIds.size();
1165 for (int i = 0; i < N; i++) {
1166 AppWidgetId id = mAppWidgetIds.get(i);
1167 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1168 return id;
1169 }
1170 }
1171 return null;
1172 }
1173
1174 Provider lookupProviderLocked(ComponentName provider) {
1175 final int N = mInstalledProviders.size();
1176 for (int i = 0; i < N; i++) {
1177 Provider p = mInstalledProviders.get(i);
1178 if (p.info.provider.equals(provider)) {
1179 return p;
1180 }
1181 }
1182 return null;
1183 }
1184
1185 Host lookupHostLocked(int uid, int hostId) {
1186 final int N = mHosts.size();
1187 for (int i = 0; i < N; i++) {
1188 Host h = mHosts.get(i);
1189 if (h.uid == uid && h.hostId == hostId) {
1190 return h;
1191 }
1192 }
1193 return null;
1194 }
1195
1196 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1197 final int N = mHosts.size();
1198 for (int i = 0; i < N; i++) {
1199 Host h = mHosts.get(i);
1200 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1201 return h;
1202 }
1203 }
1204 Host host = new Host();
1205 host.packageName = packageName;
1206 host.uid = uid;
1207 host.hostId = hostId;
1208 mHosts.add(host);
1209 return host;
1210 }
1211
1212 void pruneHostLocked(Host host) {
1213 if (host.instances.size() == 0 && host.callbacks == null) {
1214 mHosts.remove(host);
1215 }
1216 }
1217
1218 void loadAppWidgetList() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001219 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001220 try {
1221 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1222 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1223 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001224
Amith Yamasani483f3b02012-03-13 16:08:00 -07001225 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1226 for (int i = 0; i < N; i++) {
1227 ResolveInfo ri = broadcastReceivers.get(i);
1228 addProviderLocked(ri);
1229 }
1230 } catch (RemoteException re) {
1231 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001232 }
1233 }
1234
1235 boolean addProviderLocked(ResolveInfo ri) {
1236 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1237 return false;
1238 }
1239 if (!ri.activityInfo.isEnabled()) {
1240 return false;
1241 }
1242 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1243 ri.activityInfo.name), ri);
1244 if (p != null) {
1245 mInstalledProviders.add(p);
1246 return true;
1247 } else {
1248 return false;
1249 }
1250 }
1251
1252 void removeProviderLocked(int index, Provider p) {
1253 int N = p.instances.size();
1254 for (int i = 0; i < N; i++) {
1255 AppWidgetId id = p.instances.get(i);
1256 // Call back with empty RemoteViews
1257 updateAppWidgetInstanceLocked(id, null);
1258 // Stop telling the host about updates for this from now on
1259 cancelBroadcasts(p);
1260 // clear out references to this appWidgetId
1261 id.host.instances.remove(id);
1262 mAppWidgetIds.remove(id);
1263 id.provider = null;
1264 pruneHostLocked(id.host);
1265 id.host = null;
1266 }
1267 p.instances.clear();
1268 mInstalledProviders.remove(index);
1269 mDeletedProviders.add(p);
1270 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1271 cancelBroadcasts(p);
1272 }
1273
1274 void sendEnableIntentLocked(Provider p) {
1275 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1276 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001277 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001278 }
1279
1280 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1281 if (appWidgetIds != null && appWidgetIds.length > 0) {
1282 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1283 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1284 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001285 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001286 }
1287 }
1288
1289 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1290 if (p.info.updatePeriodMillis > 0) {
1291 // if this is the first instance, set the alarm. otherwise,
1292 // rely on the fact that we've already set it and that
1293 // PendingIntent.getBroadcast will update the extras.
1294 boolean alreadyRegistered = p.broadcast != null;
1295 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1296 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1297 intent.setComponent(p.info.provider);
1298 long token = Binder.clearCallingIdentity();
1299 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001300 p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
1301 PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001302 } finally {
1303 Binder.restoreCallingIdentity(token);
1304 }
1305 if (!alreadyRegistered) {
1306 long period = p.info.updatePeriodMillis;
1307 if (period < MIN_UPDATE_PERIOD) {
1308 period = MIN_UPDATE_PERIOD;
1309 }
1310 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1311 .elapsedRealtime()
1312 + period, period, p.broadcast);
1313 }
1314 }
1315 }
1316
1317 static int[] getAppWidgetIds(Provider p) {
1318 int instancesSize = p.instances.size();
1319 int appWidgetIds[] = new int[instancesSize];
1320 for (int i = 0; i < instancesSize; i++) {
1321 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1322 }
1323 return appWidgetIds;
1324 }
1325
1326 public int[] getAppWidgetIds(ComponentName provider) {
1327 synchronized (mAppWidgetIds) {
1328 ensureStateLoadedLocked();
1329 Provider p = lookupProviderLocked(provider);
1330 if (p != null && Binder.getCallingUid() == p.uid) {
1331 return getAppWidgetIds(p);
1332 } else {
1333 return new int[0];
1334 }
1335 }
1336 }
1337
1338 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1339 Provider p = null;
1340
1341 ActivityInfo activityInfo = ri.activityInfo;
1342 XmlResourceParser parser = null;
1343 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001344 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001345 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1346 if (parser == null) {
1347 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1348 + " meta-data for " + "AppWidget provider '" + component + '\'');
1349 return null;
1350 }
1351
1352 AttributeSet attrs = Xml.asAttributeSet(parser);
1353
1354 int type;
1355 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1356 && type != XmlPullParser.START_TAG) {
1357 // drain whitespace, comments, etc.
1358 }
1359
1360 String nodeName = parser.getName();
1361 if (!"appwidget-provider".equals(nodeName)) {
1362 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1363 + " AppWidget provider '" + component + '\'');
1364 return null;
1365 }
1366
1367 p = new Provider();
1368 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1369 info.provider = component;
1370 p.uid = activityInfo.applicationInfo.uid;
1371
Amith Yamasani483f3b02012-03-13 16:08:00 -07001372 Resources res = mContext.getPackageManager()
Amith Yamasani8320de82012-10-05 16:10:38 -07001373 .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001374
1375 TypedArray sa = res.obtainAttributes(attrs,
1376 com.android.internal.R.styleable.AppWidgetProviderInfo);
1377
1378 // These dimensions has to be resolved in the application's context.
1379 // We simply send back the raw complex data, which will be
1380 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1381 TypedValue value = sa
1382 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1383 info.minWidth = value != null ? value.data : 0;
1384 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1385 info.minHeight = value != null ? value.data : 0;
1386 value = sa.peekValue(
1387 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1388 info.minResizeWidth = value != null ? value.data : info.minWidth;
1389 value = sa.peekValue(
1390 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1391 info.minResizeHeight = value != null ? value.data : info.minHeight;
1392 info.updatePeriodMillis = sa.getInt(
1393 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1394 info.initialLayout = sa.getResourceId(
1395 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001396 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1397 AppWidgetProviderInfo_initialKeyguardLayout, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001398 String className = sa
1399 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1400 if (className != null) {
1401 info.configure = new ComponentName(component.getPackageName(), className);
1402 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001403 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001404 info.icon = ri.getIconResource();
1405 info.previewImage = sa.getResourceId(
1406 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1407 info.autoAdvanceViewId = sa.getResourceId(
1408 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1409 info.resizeMode = sa.getInt(
1410 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1411 AppWidgetProviderInfo.RESIZE_NONE);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001412 info.widgetCategory = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001413 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001414 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
1415 info.widgetFeatures = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001416 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001417 AppWidgetProviderInfo.WIDGET_FEATURES_NONE);
Amith Yamasani742a6712011-05-04 14:49:28 -07001418
1419 sa.recycle();
1420 } catch (Exception e) {
1421 // Ok to catch Exception here, because anything going wrong because
1422 // of what a client process passes to us should not be fatal for the
1423 // system process.
1424 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1425 return null;
1426 } finally {
1427 if (parser != null)
1428 parser.close();
1429 }
1430 return p;
1431 }
1432
1433 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001434 PackageInfo pkgInfo = null;
1435 try {
1436 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1437 } catch (RemoteException re) {
1438 // Shouldn't happen, local call
1439 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001440 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1441 throw new PackageManager.NameNotFoundException();
1442 }
1443 return pkgInfo.applicationInfo.uid;
1444 }
1445
Jim Millerf229e4d32012-09-12 20:32:50 -07001446 int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1447 int callingUid = Binder.getCallingUid();
Michael Jurka03bdc8a2012-09-21 16:10:21 -07001448 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
Jim Millerf229e4d32012-09-12 20:32:50 -07001449 return callingUid;
1450 }
1451 return enforceCallingUid(packageName);
1452 }
1453
Amith Yamasani742a6712011-05-04 14:49:28 -07001454 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1455 int callingUid = Binder.getCallingUid();
1456 int packageUid;
1457 try {
1458 packageUid = getUidForPackage(packageName);
1459 } catch (PackageManager.NameNotFoundException ex) {
1460 throw new IllegalArgumentException("packageName and uid don't match packageName="
1461 + packageName);
1462 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001463 if (!UserHandle.isSameApp(callingUid, packageUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001464 throw new IllegalArgumentException("packageName and uid don't match packageName="
1465 + packageName);
1466 }
1467 return callingUid;
1468 }
1469
1470 void sendInitialBroadcasts() {
1471 synchronized (mAppWidgetIds) {
1472 ensureStateLoadedLocked();
1473 final int N = mInstalledProviders.size();
1474 for (int i = 0; i < N; i++) {
1475 Provider p = mInstalledProviders.get(i);
1476 if (p.instances.size() > 0) {
1477 sendEnableIntentLocked(p);
1478 int[] appWidgetIds = getAppWidgetIds(p);
1479 sendUpdateIntentLocked(p, appWidgetIds);
1480 registerForBroadcastsLocked(p, appWidgetIds);
1481 }
1482 }
1483 }
1484 }
1485
1486 // only call from initialization -- it assumes that the data structures are all empty
1487 void loadStateLocked() {
1488 AtomicFile file = savedStateFile();
1489 try {
1490 FileInputStream stream = file.openRead();
1491 readStateFromFileLocked(stream);
1492
1493 if (stream != null) {
1494 try {
1495 stream.close();
1496 } catch (IOException e) {
1497 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1498 }
1499 }
1500 } catch (FileNotFoundException e) {
1501 Slog.w(TAG, "Failed to read state: " + e);
1502 }
1503 }
1504
1505 void saveStateLocked() {
1506 AtomicFile file = savedStateFile();
1507 FileOutputStream stream;
1508 try {
1509 stream = file.startWrite();
1510 if (writeStateToFileLocked(stream)) {
1511 file.finishWrite(stream);
1512 } else {
1513 file.failWrite(stream);
1514 Slog.w(TAG, "Failed to save state, restoring backup.");
1515 }
1516 } catch (IOException e) {
1517 Slog.w(TAG, "Failed open state file for write: " + e);
1518 }
1519 }
1520
1521 boolean writeStateToFileLocked(FileOutputStream stream) {
1522 int N;
1523
1524 try {
1525 XmlSerializer out = new FastXmlSerializer();
1526 out.setOutput(stream, "utf-8");
1527 out.startDocument(null, true);
1528 out.startTag(null, "gs");
1529
1530 int providerIndex = 0;
1531 N = mInstalledProviders.size();
1532 for (int i = 0; i < N; i++) {
1533 Provider p = mInstalledProviders.get(i);
1534 if (p.instances.size() > 0) {
1535 out.startTag(null, "p");
1536 out.attribute(null, "pkg", p.info.provider.getPackageName());
1537 out.attribute(null, "cl", p.info.provider.getClassName());
1538 out.endTag(null, "p");
1539 p.tag = providerIndex;
1540 providerIndex++;
1541 }
1542 }
1543
1544 N = mHosts.size();
1545 for (int i = 0; i < N; i++) {
1546 Host host = mHosts.get(i);
1547 out.startTag(null, "h");
1548 out.attribute(null, "pkg", host.packageName);
1549 out.attribute(null, "id", Integer.toHexString(host.hostId));
1550 out.endTag(null, "h");
1551 host.tag = i;
1552 }
1553
1554 N = mAppWidgetIds.size();
1555 for (int i = 0; i < N; i++) {
1556 AppWidgetId id = mAppWidgetIds.get(i);
1557 out.startTag(null, "g");
1558 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1559 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1560 if (id.provider != null) {
1561 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1562 }
Adam Cohen0aa2d422012-09-07 17:37:26 -07001563 if (id.options != null) {
1564 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1565 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1566 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1567 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1568 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1569 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1570 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1571 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1572 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1573 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1574 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001575 out.endTag(null, "g");
1576 }
1577
Michael Jurka61a5b012012-04-13 10:39:45 -07001578 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1579 while (it.hasNext()) {
1580 out.startTag(null, "b");
1581 out.attribute(null, "packageName", it.next());
1582 out.endTag(null, "b");
1583 }
1584
Amith Yamasani742a6712011-05-04 14:49:28 -07001585 out.endTag(null, "gs");
1586
1587 out.endDocument();
1588 return true;
1589 } catch (IOException e) {
1590 Slog.w(TAG, "Failed to write state: " + e);
1591 return false;
1592 }
1593 }
1594
Adam Cohen0aa2d422012-09-07 17:37:26 -07001595 @SuppressWarnings("unused")
Amith Yamasani742a6712011-05-04 14:49:28 -07001596 void readStateFromFileLocked(FileInputStream stream) {
1597 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001598 try {
1599 XmlPullParser parser = Xml.newPullParser();
1600 parser.setInput(stream, null);
1601
1602 int type;
1603 int providerIndex = 0;
1604 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1605 do {
1606 type = parser.next();
1607 if (type == XmlPullParser.START_TAG) {
1608 String tag = parser.getName();
1609 if ("p".equals(tag)) {
1610 // TODO: do we need to check that this package has the same signature
1611 // as before?
1612 String pkg = parser.getAttributeValue(null, "pkg");
1613 String cl = parser.getAttributeValue(null, "cl");
1614
Amith Yamasanif203aee2012-08-29 18:41:53 -07001615 final IPackageManager packageManager = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -07001616 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001617 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
Amith Yamasanif203aee2012-08-29 18:41:53 -07001618 } catch (RemoteException e) {
1619 String[] pkgs = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001620 .currentToCanonicalPackageNames(new String[] { pkg });
1621 pkg = pkgs[0];
1622 }
1623
1624 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1625 if (p == null && mSafeMode) {
1626 // if we're in safe mode, make a temporary one
1627 p = new Provider();
1628 p.info = new AppWidgetProviderInfo();
1629 p.info.provider = new ComponentName(pkg, cl);
1630 p.zombie = true;
1631 mInstalledProviders.add(p);
1632 }
1633 if (p != null) {
1634 // if it wasn't uninstalled or something
1635 loadedProviders.put(providerIndex, p);
1636 }
1637 providerIndex++;
1638 } else if ("h".equals(tag)) {
1639 Host host = new Host();
1640
1641 // TODO: do we need to check that this package has the same signature
1642 // as before?
1643 host.packageName = parser.getAttributeValue(null, "pkg");
1644 try {
1645 host.uid = getUidForPackage(host.packageName);
1646 } catch (PackageManager.NameNotFoundException ex) {
1647 host.zombie = true;
1648 }
1649 if (!host.zombie || mSafeMode) {
1650 // In safe mode, we don't discard the hosts we don't recognize
1651 // so that they're not pruned from our list. Otherwise, we do.
1652 host.hostId = Integer
1653 .parseInt(parser.getAttributeValue(null, "id"), 16);
1654 mHosts.add(host);
1655 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001656 } else if ("b".equals(tag)) {
1657 String packageName = parser.getAttributeValue(null, "packageName");
1658 if (packageName != null) {
1659 mPackagesWithBindWidgetPermission.add(packageName);
1660 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001661 } else if ("g".equals(tag)) {
1662 AppWidgetId id = new AppWidgetId();
1663 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1664 if (id.appWidgetId >= mNextAppWidgetId) {
1665 mNextAppWidgetId = id.appWidgetId + 1;
1666 }
1667
Adam Cohen0aa2d422012-09-07 17:37:26 -07001668 Bundle options = new Bundle();
1669 String minWidthString = parser.getAttributeValue(null, "min_width");
1670 if (minWidthString != null) {
1671 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1672 Integer.parseInt(minWidthString, 16));
1673 }
1674 String minHeightString = parser.getAttributeValue(null, "min_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001675 if (minHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001676 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1677 Integer.parseInt(minHeightString, 16));
1678 }
Adam Cohendb38d8a2012-09-21 18:14:58 -07001679 String maxWidthString = parser.getAttributeValue(null, "max_width");
1680 if (maxWidthString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001681 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1682 Integer.parseInt(maxWidthString, 16));
1683 }
1684 String maxHeightString = parser.getAttributeValue(null, "max_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001685 if (maxHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001686 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1687 Integer.parseInt(maxHeightString, 16));
1688 }
1689 String categoryString = parser.getAttributeValue(null, "host_category");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001690 if (categoryString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001691 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1692 Integer.parseInt(categoryString, 16));
1693 }
1694 id.options = options;
1695
Amith Yamasani742a6712011-05-04 14:49:28 -07001696 String providerString = parser.getAttributeValue(null, "p");
1697 if (providerString != null) {
1698 // there's no provider if it hasn't been bound yet.
1699 // maybe we don't have to save this, but it brings the system
1700 // to the state it was in.
1701 int pIndex = Integer.parseInt(providerString, 16);
1702 id.provider = loadedProviders.get(pIndex);
1703 if (false) {
1704 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1705 + pIndex + " which is " + id.provider);
1706 }
1707 if (id.provider == null) {
1708 // This provider is gone. We just let the host figure out
1709 // that this happened when it fails to load it.
1710 continue;
1711 }
1712 }
1713
1714 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1715 id.host = mHosts.get(hIndex);
1716 if (id.host == null) {
1717 // This host is gone.
1718 continue;
1719 }
1720
1721 if (id.provider != null) {
1722 id.provider.instances.add(id);
1723 }
1724 id.host.instances.add(id);
1725 mAppWidgetIds.add(id);
1726 }
1727 }
1728 } while (type != XmlPullParser.END_DOCUMENT);
1729 success = true;
1730 } catch (NullPointerException e) {
1731 Slog.w(TAG, "failed parsing " + e);
1732 } catch (NumberFormatException e) {
1733 Slog.w(TAG, "failed parsing " + e);
1734 } catch (XmlPullParserException e) {
1735 Slog.w(TAG, "failed parsing " + e);
1736 } catch (IOException e) {
1737 Slog.w(TAG, "failed parsing " + e);
1738 } catch (IndexOutOfBoundsException e) {
1739 Slog.w(TAG, "failed parsing " + e);
1740 }
1741
1742 if (success) {
1743 // delete any hosts that didn't manage to get connected (should happen)
1744 // if it matters, they'll be reconnected.
1745 for (int i = mHosts.size() - 1; i >= 0; i--) {
1746 pruneHostLocked(mHosts.get(i));
1747 }
1748 } else {
1749 // failed reading, clean up
1750 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1751
1752 mAppWidgetIds.clear();
1753 mHosts.clear();
1754 final int N = mInstalledProviders.size();
1755 for (int i = 0; i < N; i++) {
1756 mInstalledProviders.get(i).instances.clear();
1757 }
1758 }
1759 }
1760
Amith Yamasani13593602012-03-22 16:16:17 -07001761 static File getSettingsFile(int userId) {
Amith Yamasani61f57372012-08-31 12:12:28 -07001762 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
Amith Yamasani13593602012-03-22 16:16:17 -07001763 }
1764
Amith Yamasani742a6712011-05-04 14:49:28 -07001765 AtomicFile savedStateFile() {
Amith Yamasani61f57372012-08-31 12:12:28 -07001766 File dir = Environment.getUserSystemDirectory(mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001767 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001768 if (!settingsFile.exists() && mUserId == 0) {
1769 if (!dir.exists()) {
1770 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001771 }
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001772 // Migrate old data
1773 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1774 // Method doesn't throw an exception on failure. Ignore any errors
1775 // in moving the file (like non-existence)
1776 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001777 }
1778 return new AtomicFile(settingsFile);
1779 }
1780
Amith Yamasani756901d2012-10-12 12:30:07 -07001781 void onUserStopping() {
Amith Yamasani13593602012-03-22 16:16:17 -07001782 // prune the ones we don't want to keep
1783 int N = mInstalledProviders.size();
1784 for (int i = N - 1; i >= 0; i--) {
1785 Provider p = mInstalledProviders.get(i);
1786 cancelBroadcasts(p);
1787 }
Amith Yamasani756901d2012-10-12 12:30:07 -07001788 }
1789
1790 void onUserRemoved() {
Amith Yamasani13593602012-03-22 16:16:17 -07001791 getSettingsFile(mUserId).delete();
1792 }
1793
Winson Chung7fbd2842012-06-13 10:35:51 -07001794 boolean addProvidersForPackageLocked(String pkgName) {
1795 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001796 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1797 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001798 List<ResolveInfo> broadcastReceivers;
1799 try {
1800 broadcastReceivers = mPm.queryIntentReceivers(intent,
1801 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1802 PackageManager.GET_META_DATA, mUserId);
1803 } catch (RemoteException re) {
1804 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001805 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001806 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001807 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1808 for (int i = 0; i < N; i++) {
1809 ResolveInfo ri = broadcastReceivers.get(i);
1810 ActivityInfo ai = ri.activityInfo;
1811 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1812 continue;
1813 }
1814 if (pkgName.equals(ai.packageName)) {
1815 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001816 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001817 }
1818 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001819
1820 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001821 }
1822
Winson Chunga3195052012-06-25 10:02:10 -07001823 /**
1824 * Updates all providers with the specified package names, and records any providers that were
1825 * pruned.
1826 *
1827 * @return whether any providers were updated
1828 */
1829 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001830 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001831 HashSet<String> keep = new HashSet<String>();
1832 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1833 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001834 List<ResolveInfo> broadcastReceivers;
1835 try {
1836 broadcastReceivers = mPm.queryIntentReceivers(intent,
1837 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1838 PackageManager.GET_META_DATA, mUserId);
1839 } catch (RemoteException re) {
1840 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001841 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001842 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001843
1844 // add the missing ones and collect which ones to keep
1845 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1846 for (int i = 0; i < N; i++) {
1847 ResolveInfo ri = broadcastReceivers.get(i);
1848 ActivityInfo ai = ri.activityInfo;
1849 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1850 continue;
1851 }
1852 if (pkgName.equals(ai.packageName)) {
1853 ComponentName component = new ComponentName(ai.packageName, ai.name);
1854 Provider p = lookupProviderLocked(component);
1855 if (p == null) {
1856 if (addProviderLocked(ri)) {
1857 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001858 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001859 }
1860 } else {
1861 Provider parsed = parseProviderInfoXml(component, ri);
1862 if (parsed != null) {
1863 keep.add(ai.name);
1864 // Use the new AppWidgetProviderInfo.
1865 p.info = parsed.info;
1866 // If it's enabled
1867 final int M = p.instances.size();
1868 if (M > 0) {
1869 int[] appWidgetIds = getAppWidgetIds(p);
1870 // Reschedule for the new updatePeriodMillis (don't worry about handling
1871 // it specially if updatePeriodMillis didn't change because we just sent
1872 // an update, and the next one will be updatePeriodMillis from now).
1873 cancelBroadcasts(p);
1874 registerForBroadcastsLocked(p, appWidgetIds);
1875 // If it's currently showing, call back with the new
1876 // AppWidgetProviderInfo.
1877 for (int j = 0; j < M; j++) {
1878 AppWidgetId id = p.instances.get(j);
1879 id.views = null;
1880 if (id.host != null && id.host.callbacks != null) {
1881 try {
1882 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1883 } catch (RemoteException ex) {
1884 // It failed; remove the callback. No need to prune because
1885 // we know that this host is still referenced by this
1886 // instance.
1887 id.host.callbacks = null;
1888 }
1889 }
1890 }
1891 // Now that we've told the host, push out an update.
1892 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07001893 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001894 }
1895 }
1896 }
1897 }
1898 }
1899
1900 // prune the ones we don't want to keep
1901 N = mInstalledProviders.size();
1902 for (int i = N - 1; i >= 0; i--) {
1903 Provider p = mInstalledProviders.get(i);
1904 if (pkgName.equals(p.info.provider.getPackageName())
1905 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07001906 if (removedProviders != null) {
1907 removedProviders.add(p.info.provider);
1908 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001909 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001910 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001911 }
1912 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001913
1914 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07001915 }
1916
Winson Chung7fbd2842012-06-13 10:35:51 -07001917 boolean removeProvidersForPackageLocked(String pkgName) {
1918 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001919 int N = mInstalledProviders.size();
1920 for (int i = N - 1; i >= 0; i--) {
1921 Provider p = mInstalledProviders.get(i);
1922 if (pkgName.equals(p.info.provider.getPackageName())) {
1923 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001924 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001925 }
1926 }
1927
1928 // Delete the hosts for this package too
1929 //
1930 // By now, we have removed any AppWidgets that were in any hosts here,
1931 // so we don't need to worry about sending DISABLE broadcasts to them.
1932 N = mHosts.size();
1933 for (int i = N - 1; i >= 0; i--) {
1934 Host host = mHosts.get(i);
1935 if (pkgName.equals(host.packageName)) {
1936 deleteHostLocked(host);
1937 }
1938 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001939
1940 return providersRemoved;
1941 }
1942
1943 void notifyHostsForProvidersChangedLocked() {
1944 final int N = mHosts.size();
1945 for (int i = N - 1; i >= 0; i--) {
1946 Host host = mHosts.get(i);
1947 try {
1948 if (host.callbacks != null) {
1949 host.callbacks.providersChanged();
1950 }
1951 } catch (RemoteException ex) {
1952 // It failed; remove the callback. No need to prune because
1953 // we know that this host is still referenced by this
1954 // instance.
1955 host.callbacks = null;
1956 }
1957 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001958 }
1959}