blob: daa82f2928d4bc1bddd0d0387c363365b428a90a [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
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);
Amith Yamasani742a6712011-05-04 14:49:28 -0700329 pw.print(" autoAdvanceViewId=");
330 pw.print(info.autoAdvanceViewId);
331 pw.print(" initialLayout=#");
332 pw.print(Integer.toHexString(info.initialLayout));
333 pw.print(" zombie="); pw.println(p.zombie);
334 }
335
336 private void dumpHost(Host host, int index, PrintWriter pw) {
337 pw.print(" ["); pw.print(index); pw.print("] hostId=");
338 pw.print(host.hostId); pw.print(' ');
339 pw.print(host.packageName); pw.print('/');
340 pw.print(host.uid); pw.println(':');
341 pw.print(" callbacks="); pw.println(host.callbacks);
342 pw.print(" instances.size="); pw.print(host.instances.size());
343 pw.print(" zombie="); pw.println(host.zombie);
344 }
345
346 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
347 pw.print(" ["); pw.print(index); pw.print("] id=");
348 pw.println(id.appWidgetId);
349 pw.print(" hostId=");
350 pw.print(id.host.hostId); pw.print(' ');
351 pw.print(id.host.packageName); pw.print('/');
352 pw.println(id.host.uid);
353 if (id.provider != null) {
354 pw.print(" provider=");
355 pw.println(id.provider.info.provider.flattenToShortString());
356 }
357 if (id.host != null) {
358 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
359 }
360 if (id.views != null) {
361 pw.print(" views="); pw.println(id.views);
362 }
363 }
364
365 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
366 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
367 != PackageManager.PERMISSION_GRANTED) {
368 pw.println("Permission Denial: can't dump from from pid="
369 + Binder.getCallingPid()
370 + ", uid=" + Binder.getCallingUid());
371 return;
372 }
373
374 synchronized (mAppWidgetIds) {
375 int N = mInstalledProviders.size();
376 pw.println("Providers:");
377 for (int i=0; i<N; i++) {
378 dumpProvider(mInstalledProviders.get(i), i, pw);
379 }
380
381 N = mAppWidgetIds.size();
382 pw.println(" ");
383 pw.println("AppWidgetIds:");
384 for (int i=0; i<N; i++) {
385 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
386 }
387
388 N = mHosts.size();
389 pw.println(" ");
390 pw.println("Hosts:");
391 for (int i=0; i<N; i++) {
392 dumpHost(mHosts.get(i), i, pw);
393 }
394
395 N = mDeletedProviders.size();
396 pw.println(" ");
397 pw.println("Deleted Providers:");
398 for (int i=0; i<N; i++) {
399 dumpProvider(mDeletedProviders.get(i), i, pw);
400 }
401
402 N = mDeletedHosts.size();
403 pw.println(" ");
404 pw.println("Deleted Hosts:");
405 for (int i=0; i<N; i++) {
406 dumpHost(mDeletedHosts.get(i), i, pw);
407 }
408 }
409 }
410
411 private void ensureStateLoadedLocked() {
412 if (!mStateLoaded) {
413 loadAppWidgetList();
414 loadStateLocked();
415 mStateLoaded = true;
416 }
417 }
418
419 public int allocateAppWidgetId(String packageName, int hostId) {
Jim Millerf229e4d2012-09-12 20:32:50 -0700420 int callingUid = enforceSystemOrCallingUid(packageName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700421 synchronized (mAppWidgetIds) {
422 ensureStateLoadedLocked();
423 int appWidgetId = mNextAppWidgetId++;
424
425 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
426
427 AppWidgetId id = new AppWidgetId();
428 id.appWidgetId = appWidgetId;
429 id.host = host;
430
431 host.instances.add(id);
432 mAppWidgetIds.add(id);
433
434 saveStateLocked();
Amith Yamasani8320de82012-10-05 16:10:38 -0700435 if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
436 + " id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700437 return appWidgetId;
438 }
439 }
440
441 public void deleteAppWidgetId(int appWidgetId) {
442 synchronized (mAppWidgetIds) {
443 ensureStateLoadedLocked();
444 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
445 if (id != null) {
446 deleteAppWidgetLocked(id);
447 saveStateLocked();
448 }
449 }
450 }
451
452 public void deleteHost(int hostId) {
453 synchronized (mAppWidgetIds) {
454 ensureStateLoadedLocked();
455 int callingUid = Binder.getCallingUid();
456 Host host = lookupHostLocked(callingUid, hostId);
457 if (host != null) {
458 deleteHostLocked(host);
459 saveStateLocked();
460 }
461 }
462 }
463
464 public void deleteAllHosts() {
465 synchronized (mAppWidgetIds) {
466 ensureStateLoadedLocked();
467 int callingUid = Binder.getCallingUid();
468 final int N = mHosts.size();
469 boolean changed = false;
470 for (int i = N - 1; i >= 0; i--) {
471 Host host = mHosts.get(i);
472 if (host.uid == callingUid) {
473 deleteHostLocked(host);
474 changed = true;
475 }
476 }
477 if (changed) {
478 saveStateLocked();
479 }
480 }
481 }
482
483 void deleteHostLocked(Host host) {
484 final int N = host.instances.size();
485 for (int i = N - 1; i >= 0; i--) {
486 AppWidgetId id = host.instances.get(i);
487 deleteAppWidgetLocked(id);
488 }
489 host.instances.clear();
490 mHosts.remove(host);
491 mDeletedHosts.add(host);
492 // it's gone or going away, abruptly drop the callback connection
493 host.callbacks = null;
494 }
495
496 void deleteAppWidgetLocked(AppWidgetId id) {
497 // We first unbind all services that are bound to this id
498 unbindAppWidgetRemoteViewsServicesLocked(id);
499
500 Host host = id.host;
501 host.instances.remove(id);
502 pruneHostLocked(host);
503
504 mAppWidgetIds.remove(id);
505
506 Provider p = id.provider;
507 if (p != null) {
508 p.instances.remove(id);
509 if (!p.zombie) {
510 // send the broacast saying that this appWidgetId has been deleted
511 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
512 intent.setComponent(p.info.provider);
513 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700514 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700515 if (p.instances.size() == 0) {
516 // cancel the future updates
517 cancelBroadcasts(p);
518
519 // send the broacast saying that the provider is not in use any more
520 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
521 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700522 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700523 }
524 }
525 }
526 }
527
528 void cancelBroadcasts(Provider p) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700529 if (DBG) log("cancelBroadcasts for " + p);
Amith Yamasani742a6712011-05-04 14:49:28 -0700530 if (p.broadcast != null) {
531 mAlarmManager.cancel(p.broadcast);
532 long token = Binder.clearCallingIdentity();
533 try {
534 p.broadcast.cancel();
535 } finally {
536 Binder.restoreCallingIdentity(token);
537 }
538 p.broadcast = null;
539 }
540 }
541
Adam Cohen0aa2d422012-09-07 17:37:26 -0700542 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700543 if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
544 + " provider=" + provider);
Amith Yamasani742a6712011-05-04 14:49:28 -0700545 final long ident = Binder.clearCallingIdentity();
546 try {
547 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700548 options = cloneIfLocalBinder(options);
Amith Yamasani742a6712011-05-04 14:49:28 -0700549 ensureStateLoadedLocked();
550 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
551 if (id == null) {
552 throw new IllegalArgumentException("bad appWidgetId");
553 }
554 if (id.provider != null) {
555 throw new IllegalArgumentException("appWidgetId " + appWidgetId
556 + " already bound to " + id.provider.info.provider);
557 }
558 Provider p = lookupProviderLocked(provider);
559 if (p == null) {
560 throw new IllegalArgumentException("not a appwidget provider: " + provider);
561 }
562 if (p.zombie) {
563 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
564 + " safe mode: " + provider);
565 }
566
Amith Yamasani742a6712011-05-04 14:49:28 -0700567 id.provider = p;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700568 if (options == null) {
569 options = new Bundle();
570 }
571 id.options = options;
572
573 // We need to provide a default value for the widget category if it is not specified
574 if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
575 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
576 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
577 }
578
Amith Yamasani742a6712011-05-04 14:49:28 -0700579 p.instances.add(id);
580 int instancesSize = p.instances.size();
581 if (instancesSize == 1) {
582 // tell the provider that it's ready
583 sendEnableIntentLocked(p);
584 }
585
586 // send an update now -- We need this update now, and just for this appWidgetId.
587 // It's less critical when the next one happens, so when we schedule the next one,
588 // we add updatePeriodMillis to its start time. That time will have some slop,
589 // but that's okay.
590 sendUpdateIntentLocked(p, new int[] { appWidgetId });
591
592 // schedule the future updates
593 registerForBroadcastsLocked(p, getAppWidgetIds(p));
594 saveStateLocked();
595 }
596 } finally {
597 Binder.restoreCallingIdentity(ident);
598 }
599 }
600
Adam Cohen0aa2d422012-09-07 17:37:26 -0700601 public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka67a871d2012-11-01 18:26:01 -0700602 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET,
Michael Jurka61a5b012012-04-13 10:39:45 -0700603 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700604 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700605 }
606
607 public boolean bindAppWidgetIdIfAllowed(
Adam Cohen0aa2d422012-09-07 17:37:26 -0700608 String packageName, int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700609 try {
Michael Jurka67a871d2012-11-01 18:26:01 -0700610 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
Michael Jurka61a5b012012-04-13 10:39:45 -0700611 } catch (SecurityException se) {
612 if (!callerHasBindAppWidgetPermission(packageName)) {
613 return false;
614 }
615 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700616 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700617 return true;
618 }
619
620 private boolean callerHasBindAppWidgetPermission(String packageName) {
621 int callingUid = Binder.getCallingUid();
622 try {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700623 if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700624 return false;
625 }
626 } catch (Exception e) {
627 return false;
628 }
629 synchronized (mAppWidgetIds) {
630 ensureStateLoadedLocked();
631 return mPackagesWithBindWidgetPermission.contains(packageName);
632 }
633 }
634
635 public boolean hasBindAppWidgetPermission(String packageName) {
636 mContext.enforceCallingPermission(
637 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
638 "hasBindAppWidgetPermission packageName=" + packageName);
639
640 synchronized (mAppWidgetIds) {
641 ensureStateLoadedLocked();
642 return mPackagesWithBindWidgetPermission.contains(packageName);
643 }
644 }
645
646 public void setBindAppWidgetPermission(String packageName, boolean permission) {
647 mContext.enforceCallingPermission(
648 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
649 "setBindAppWidgetPermission packageName=" + packageName);
650
651 synchronized (mAppWidgetIds) {
652 ensureStateLoadedLocked();
653 if (permission) {
654 mPackagesWithBindWidgetPermission.add(packageName);
655 } else {
656 mPackagesWithBindWidgetPermission.remove(packageName);
657 }
658 }
659 saveStateLocked();
660 }
661
Amith Yamasani742a6712011-05-04 14:49:28 -0700662 // Binds to a specific RemoteViewsService
663 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
664 synchronized (mAppWidgetIds) {
665 ensureStateLoadedLocked();
666 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
667 if (id == null) {
668 throw new IllegalArgumentException("bad appWidgetId");
669 }
670 final ComponentName componentName = intent.getComponent();
671 try {
Amith Yamasani98edc952012-09-25 14:09:27 -0700672 final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
673 PackageManager.GET_PERMISSIONS, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700674 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
675 throw new SecurityException("Selected service does not require "
676 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
677 }
Amith Yamasani98edc952012-09-25 14:09:27 -0700678 } catch (RemoteException e) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700679 throw new IllegalArgumentException("Unknown component " + componentName);
680 }
681
682 // If there is already a connection made for this service intent, then disconnect from
683 // that first. (This does not allow multiple connections to the same service under
684 // the same key)
685 ServiceConnectionProxy conn = null;
686 FilterComparison fc = new FilterComparison(intent);
687 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
688 if (mBoundRemoteViewsServices.containsKey(key)) {
689 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
690 conn.disconnect();
691 mContext.unbindService(conn);
692 mBoundRemoteViewsServices.remove(key);
693 }
694
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700695 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700696 // Bind to the RemoteViewsService (which will trigger a callback to the
697 // RemoteViewsAdapter.onServiceConnected())
698 final long token = Binder.clearCallingIdentity();
699 try {
700 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800701 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700702 mBoundRemoteViewsServices.put(key, conn);
703 } finally {
704 Binder.restoreCallingIdentity(token);
705 }
706
707 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
708 // when we can call back to the RemoteViewsService later to destroy associated
709 // factories.
710 incrementAppWidgetServiceRefCount(appWidgetId, fc);
711 }
712 }
713
714 // Unbinds from a specific RemoteViewsService
715 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
716 synchronized (mAppWidgetIds) {
717 ensureStateLoadedLocked();
718 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
719 // RemoteViewsAdapter)
720 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
721 intent));
722 if (mBoundRemoteViewsServices.containsKey(key)) {
723 // We don't need to use the appWidgetId until after we are sure there is something
724 // to unbind. Note that this may mask certain issues with apps calling unbind()
725 // more than necessary.
726 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
727 if (id == null) {
728 throw new IllegalArgumentException("bad appWidgetId");
729 }
730
731 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
732 .get(key);
733 conn.disconnect();
734 mContext.unbindService(conn);
735 mBoundRemoteViewsServices.remove(key);
736 } else {
737 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
738 }
739 }
740 }
741
742 // Unbinds from a RemoteViewsService when we delete an app widget
743 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
744 int appWidgetId = id.appWidgetId;
745 // Unbind all connections to Services bound to this AppWidgetId
746 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
747 .iterator();
748 while (it.hasNext()) {
749 final Pair<Integer, Intent.FilterComparison> key = it.next();
750 if (key.first.intValue() == appWidgetId) {
751 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
752 .get(key);
753 conn.disconnect();
754 mContext.unbindService(conn);
755 it.remove();
756 }
757 }
758
759 // Check if we need to destroy any services (if no other app widgets are
760 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800761 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700762 }
763
764 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800765 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700766 final ServiceConnection conn = new ServiceConnection() {
767 @Override
768 public void onServiceConnected(ComponentName name, IBinder service) {
769 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
770 try {
771 cb.onDestroy(intent);
772 } catch (RemoteException e) {
773 e.printStackTrace();
774 } catch (RuntimeException e) {
775 e.printStackTrace();
776 }
777 mContext.unbindService(this);
778 }
779
780 @Override
781 public void onServiceDisconnected(android.content.ComponentName name) {
782 // Do nothing
783 }
784 };
785
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700786 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700787 // Bind to the service and remove the static intent->factory mapping in the
788 // RemoteViewsService.
789 final long token = Binder.clearCallingIdentity();
790 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800791 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700792 } finally {
793 Binder.restoreCallingIdentity(token);
794 }
795 }
796
797 // Adds to the ref-count for a given RemoteViewsService intent
798 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
799 HashSet<Integer> appWidgetIds = null;
800 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
801 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
802 } else {
803 appWidgetIds = new HashSet<Integer>();
804 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
805 }
806 appWidgetIds.add(appWidgetId);
807 }
808
809 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
810 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800811 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700812 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
813 while (it.hasNext()) {
814 final FilterComparison key = it.next();
815 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800816 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700817 // If we have removed the last app widget referencing this service, then we
818 // should destroy it and remove it from this set
819 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800820 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700821 it.remove();
822 }
823 }
824 }
825 }
826
827 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
828 synchronized (mAppWidgetIds) {
829 ensureStateLoadedLocked();
830 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
831 if (id != null && id.provider != null && !id.provider.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700832 return cloneIfLocalBinder(id.provider.info);
Amith Yamasani742a6712011-05-04 14:49:28 -0700833 }
834 return null;
835 }
836 }
837
838 public RemoteViews getAppWidgetViews(int appWidgetId) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700839 if (DBG) log("getAppWidgetViews id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700840 synchronized (mAppWidgetIds) {
841 ensureStateLoadedLocked();
842 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
843 if (id != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700844 return cloneIfLocalBinder(id.views);
Amith Yamasani742a6712011-05-04 14:49:28 -0700845 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700846 if (DBG) log(" couldn't find appwidgetid");
Amith Yamasani742a6712011-05-04 14:49:28 -0700847 return null;
848 }
849 }
850
851 public List<AppWidgetProviderInfo> getInstalledProviders() {
852 synchronized (mAppWidgetIds) {
853 ensureStateLoadedLocked();
854 final int N = mInstalledProviders.size();
855 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
856 for (int i = 0; i < N; i++) {
857 Provider p = mInstalledProviders.get(i);
858 if (!p.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700859 result.add(cloneIfLocalBinder(p.info));
Amith Yamasani742a6712011-05-04 14:49:28 -0700860 }
861 }
862 return result;
863 }
864 }
865
866 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
867 if (appWidgetIds == null) {
868 return;
869 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700870 if (DBG) log("updateAppWidgetIds views: " + views);
Adam Cohenf08a8b72012-07-16 12:02:10 -0700871 int bitmapMemoryUsage = 0;
872 if (views != null) {
873 bitmapMemoryUsage = views.estimateMemoryUsage();
874 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700875 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
876 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
877 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
878 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
879 " fill the device's screen once.");
880 }
881
Amith Yamasani742a6712011-05-04 14:49:28 -0700882 if (appWidgetIds.length == 0) {
883 return;
884 }
885 final int N = appWidgetIds.length;
886
887 synchronized (mAppWidgetIds) {
888 ensureStateLoadedLocked();
889 for (int i = 0; i < N; i++) {
890 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
891 updateAppWidgetInstanceLocked(id, views);
892 }
893 }
894 }
895
Adam Cohend2097eb2012-05-01 18:10:28 -0700896 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700897 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700898 options = cloneIfLocalBinder(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700899 ensureStateLoadedLocked();
900 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
901
902 if (id == null) {
903 return;
904 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700905
Adam Cohene8724c82012-04-19 17:11:40 -0700906 Provider p = id.provider;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700907 // Merge the options
908 id.options.putAll(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700909
910 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700911 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700912 intent.setComponent(p.info.provider);
913 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700914 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700915 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Adam Cohen0aa2d422012-09-07 17:37:26 -0700916 saveStateLocked();
Adam Cohene8724c82012-04-19 17:11:40 -0700917 }
918 }
919
Adam Cohend2097eb2012-05-01 18:10:28 -0700920 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -0700921 synchronized (mAppWidgetIds) {
922 ensureStateLoadedLocked();
923 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700924 if (id != null && id.options != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700925 return cloneIfLocalBinder(id.options);
Adam Cohene8724c82012-04-19 17:11:40 -0700926 } else {
927 return Bundle.EMPTY;
928 }
929 }
930 }
931
Amith Yamasani742a6712011-05-04 14:49:28 -0700932 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
933 if (appWidgetIds == null) {
934 return;
935 }
936 if (appWidgetIds.length == 0) {
937 return;
938 }
939 final int N = appWidgetIds.length;
940
941 synchronized (mAppWidgetIds) {
942 ensureStateLoadedLocked();
943 for (int i = 0; i < N; i++) {
944 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung66119882012-10-11 14:26:25 -0700945 if (id.views != null) {
946 // Only trigger a partial update for a widget if it has received a full update
947 updateAppWidgetInstanceLocked(id, views, true);
948 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700949 }
950 }
951 }
952
953 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
954 if (appWidgetIds == null) {
955 return;
956 }
957 if (appWidgetIds.length == 0) {
958 return;
959 }
960 final int N = appWidgetIds.length;
961
962 synchronized (mAppWidgetIds) {
963 ensureStateLoadedLocked();
964 for (int i = 0; i < N; i++) {
965 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
966 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
967 }
968 }
969 }
970
971 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
972 synchronized (mAppWidgetIds) {
973 ensureStateLoadedLocked();
974 Provider p = lookupProviderLocked(provider);
975 if (p == null) {
976 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
977 return;
978 }
979 ArrayList<AppWidgetId> instances = p.instances;
980 final int callingUid = Binder.getCallingUid();
981 final int N = instances.size();
982 for (int i = 0; i < N; i++) {
983 AppWidgetId id = instances.get(i);
984 if (canAccessAppWidgetId(id, callingUid)) {
985 updateAppWidgetInstanceLocked(id, views);
986 }
987 }
988 }
989 }
990
991 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
992 updateAppWidgetInstanceLocked(id, views, false);
993 }
994
995 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
996 // allow for stale appWidgetIds and other badness
997 // lookup also checks that the calling process can access the appWidgetId
998 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
999 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1000
Winson Chung66119882012-10-11 14:26:25 -07001001 if (!isPartialUpdate) {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001002 // For a full update we replace the RemoteViews completely.
Amith Yamasani742a6712011-05-04 14:49:28 -07001003 id.views = views;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001004 } else {
1005 // For a partial update, we merge the new RemoteViews with the old.
1006 id.views.mergeRemoteViews(views);
1007 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001008
1009 // is anyone listening?
1010 if (id.host.callbacks != null) {
1011 try {
1012 // the lock is held, but this is a oneway call
1013 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
1014 } catch (RemoteException e) {
1015 // It failed; remove the callback. No need to prune because
1016 // we know that this host is still referenced by this instance.
1017 id.host.callbacks = null;
1018 }
1019 }
1020 }
1021 }
1022
1023 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1024 // allow for stale appWidgetIds and other badness
1025 // lookup also checks that the calling process can access the appWidgetId
1026 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1027 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1028 // is anyone listening?
1029 if (id.host.callbacks != null) {
1030 try {
1031 // the lock is held, but this is a oneway call
1032 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1033 } catch (RemoteException e) {
1034 // It failed; remove the callback. No need to prune because
1035 // we know that this host is still referenced by this instance.
1036 id.host.callbacks = null;
1037 }
1038 }
1039
1040 // If the host is unavailable, then we call the associated
1041 // RemoteViewsFactory.onDataSetChanged() directly
1042 if (id.host.callbacks == null) {
1043 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1044 for (FilterComparison key : keys) {
1045 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1046 Intent intent = key.getIntent();
1047
1048 final ServiceConnection conn = new ServiceConnection() {
1049 @Override
1050 public void onServiceConnected(ComponentName name, IBinder service) {
1051 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1052 .asInterface(service);
1053 try {
1054 cb.onDataSetChangedAsync();
1055 } catch (RemoteException e) {
1056 e.printStackTrace();
1057 } catch (RuntimeException e) {
1058 e.printStackTrace();
1059 }
1060 mContext.unbindService(this);
1061 }
1062
1063 @Override
1064 public void onServiceDisconnected(android.content.ComponentName name) {
1065 // Do nothing
1066 }
1067 };
1068
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001069 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001070 // Bind to the service and call onDataSetChanged()
1071 final long token = Binder.clearCallingIdentity();
1072 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001073 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001074 } finally {
1075 Binder.restoreCallingIdentity(token);
1076 }
1077 }
1078 }
1079 }
1080 }
1081 }
1082
Adam Cohen3ff2d862012-09-26 14:07:57 -07001083 private boolean isLocalBinder() {
1084 return Process.myPid() == Binder.getCallingPid();
1085 }
1086
1087 private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1088 if (isLocalBinder() && rv != null) {
1089 return rv.clone();
1090 }
1091 return rv;
1092 }
1093
1094 private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
1095 if (isLocalBinder() && info != null) {
1096 return info.clone();
1097 }
1098 return info;
1099 }
1100
1101 private Bundle cloneIfLocalBinder(Bundle bundle) {
1102 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
1103 // if we start adding objects to the options. Further, it would only be an issue if keyguard
1104 // used such options.
1105 if (isLocalBinder() && bundle != null) {
1106 return (Bundle) bundle.clone();
1107 }
1108 return bundle;
1109 }
1110
Amith Yamasani742a6712011-05-04 14:49:28 -07001111 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1112 List<RemoteViews> updatedViews) {
1113 int callingUid = enforceCallingUid(packageName);
1114 synchronized (mAppWidgetIds) {
1115 ensureStateLoadedLocked();
1116 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1117 host.callbacks = callbacks;
1118
1119 updatedViews.clear();
1120
1121 ArrayList<AppWidgetId> instances = host.instances;
1122 int N = instances.size();
1123 int[] updatedIds = new int[N];
1124 for (int i = 0; i < N; i++) {
1125 AppWidgetId id = instances.get(i);
1126 updatedIds[i] = id.appWidgetId;
Adam Cohen3ff2d862012-09-26 14:07:57 -07001127 updatedViews.add(cloneIfLocalBinder(id.views));
Amith Yamasani742a6712011-05-04 14:49:28 -07001128 }
1129 return updatedIds;
1130 }
1131 }
1132
1133 public void stopListening(int hostId) {
1134 synchronized (mAppWidgetIds) {
1135 ensureStateLoadedLocked();
1136 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1137 if (host != null) {
1138 host.callbacks = null;
1139 pruneHostLocked(host);
1140 }
1141 }
1142 }
1143
1144 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1145 if (id.host.uid == callingUid) {
1146 // Apps hosting the AppWidget have access to it.
1147 return true;
1148 }
1149 if (id.provider != null && id.provider.uid == callingUid) {
1150 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1151 return true;
1152 }
1153 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1154 // Apps that can bind have access to all appWidgetIds.
1155 return true;
1156 }
1157 // Nobody else can access it.
1158 return false;
1159 }
1160
1161 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1162 int callingUid = Binder.getCallingUid();
1163 final int N = mAppWidgetIds.size();
1164 for (int i = 0; i < N; i++) {
1165 AppWidgetId id = mAppWidgetIds.get(i);
1166 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1167 return id;
1168 }
1169 }
1170 return null;
1171 }
1172
1173 Provider lookupProviderLocked(ComponentName provider) {
1174 final int N = mInstalledProviders.size();
1175 for (int i = 0; i < N; i++) {
1176 Provider p = mInstalledProviders.get(i);
1177 if (p.info.provider.equals(provider)) {
1178 return p;
1179 }
1180 }
1181 return null;
1182 }
1183
1184 Host lookupHostLocked(int uid, int hostId) {
1185 final int N = mHosts.size();
1186 for (int i = 0; i < N; i++) {
1187 Host h = mHosts.get(i);
1188 if (h.uid == uid && h.hostId == hostId) {
1189 return h;
1190 }
1191 }
1192 return null;
1193 }
1194
1195 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1196 final int N = mHosts.size();
1197 for (int i = 0; i < N; i++) {
1198 Host h = mHosts.get(i);
1199 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1200 return h;
1201 }
1202 }
1203 Host host = new Host();
1204 host.packageName = packageName;
1205 host.uid = uid;
1206 host.hostId = hostId;
1207 mHosts.add(host);
1208 return host;
1209 }
1210
1211 void pruneHostLocked(Host host) {
1212 if (host.instances.size() == 0 && host.callbacks == null) {
1213 mHosts.remove(host);
1214 }
1215 }
1216
1217 void loadAppWidgetList() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001218 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001219 try {
1220 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1221 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1222 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001223
Amith Yamasani483f3b02012-03-13 16:08:00 -07001224 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1225 for (int i = 0; i < N; i++) {
1226 ResolveInfo ri = broadcastReceivers.get(i);
1227 addProviderLocked(ri);
1228 }
1229 } catch (RemoteException re) {
1230 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001231 }
1232 }
1233
1234 boolean addProviderLocked(ResolveInfo ri) {
1235 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1236 return false;
1237 }
1238 if (!ri.activityInfo.isEnabled()) {
1239 return false;
1240 }
1241 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1242 ri.activityInfo.name), ri);
1243 if (p != null) {
1244 mInstalledProviders.add(p);
1245 return true;
1246 } else {
1247 return false;
1248 }
1249 }
1250
1251 void removeProviderLocked(int index, Provider p) {
1252 int N = p.instances.size();
1253 for (int i = 0; i < N; i++) {
1254 AppWidgetId id = p.instances.get(i);
1255 // Call back with empty RemoteViews
1256 updateAppWidgetInstanceLocked(id, null);
1257 // Stop telling the host about updates for this from now on
1258 cancelBroadcasts(p);
1259 // clear out references to this appWidgetId
1260 id.host.instances.remove(id);
1261 mAppWidgetIds.remove(id);
1262 id.provider = null;
1263 pruneHostLocked(id.host);
1264 id.host = null;
1265 }
1266 p.instances.clear();
1267 mInstalledProviders.remove(index);
1268 mDeletedProviders.add(p);
1269 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1270 cancelBroadcasts(p);
1271 }
1272
1273 void sendEnableIntentLocked(Provider p) {
1274 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1275 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001276 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001277 }
1278
1279 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1280 if (appWidgetIds != null && appWidgetIds.length > 0) {
1281 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1282 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1283 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001284 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001285 }
1286 }
1287
1288 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1289 if (p.info.updatePeriodMillis > 0) {
1290 // if this is the first instance, set the alarm. otherwise,
1291 // rely on the fact that we've already set it and that
1292 // PendingIntent.getBroadcast will update the extras.
1293 boolean alreadyRegistered = p.broadcast != null;
1294 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1295 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1296 intent.setComponent(p.info.provider);
1297 long token = Binder.clearCallingIdentity();
1298 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001299 p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
1300 PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001301 } finally {
1302 Binder.restoreCallingIdentity(token);
1303 }
1304 if (!alreadyRegistered) {
1305 long period = p.info.updatePeriodMillis;
1306 if (period < MIN_UPDATE_PERIOD) {
1307 period = MIN_UPDATE_PERIOD;
1308 }
1309 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1310 .elapsedRealtime()
1311 + period, period, p.broadcast);
1312 }
1313 }
1314 }
1315
1316 static int[] getAppWidgetIds(Provider p) {
1317 int instancesSize = p.instances.size();
1318 int appWidgetIds[] = new int[instancesSize];
1319 for (int i = 0; i < instancesSize; i++) {
1320 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1321 }
1322 return appWidgetIds;
1323 }
1324
1325 public int[] getAppWidgetIds(ComponentName provider) {
1326 synchronized (mAppWidgetIds) {
1327 ensureStateLoadedLocked();
1328 Provider p = lookupProviderLocked(provider);
1329 if (p != null && Binder.getCallingUid() == p.uid) {
1330 return getAppWidgetIds(p);
1331 } else {
1332 return new int[0];
1333 }
1334 }
1335 }
1336
1337 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1338 Provider p = null;
1339
1340 ActivityInfo activityInfo = ri.activityInfo;
1341 XmlResourceParser parser = null;
1342 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001343 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001344 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1345 if (parser == null) {
1346 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1347 + " meta-data for " + "AppWidget provider '" + component + '\'');
1348 return null;
1349 }
1350
1351 AttributeSet attrs = Xml.asAttributeSet(parser);
1352
1353 int type;
1354 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1355 && type != XmlPullParser.START_TAG) {
1356 // drain whitespace, comments, etc.
1357 }
1358
1359 String nodeName = parser.getName();
1360 if (!"appwidget-provider".equals(nodeName)) {
1361 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1362 + " AppWidget provider '" + component + '\'');
1363 return null;
1364 }
1365
1366 p = new Provider();
1367 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1368 info.provider = component;
1369 p.uid = activityInfo.applicationInfo.uid;
1370
Amith Yamasani483f3b02012-03-13 16:08:00 -07001371 Resources res = mContext.getPackageManager()
Amith Yamasani8320de82012-10-05 16:10:38 -07001372 .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001373
1374 TypedArray sa = res.obtainAttributes(attrs,
1375 com.android.internal.R.styleable.AppWidgetProviderInfo);
1376
1377 // These dimensions has to be resolved in the application's context.
1378 // We simply send back the raw complex data, which will be
1379 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1380 TypedValue value = sa
1381 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1382 info.minWidth = value != null ? value.data : 0;
1383 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1384 info.minHeight = value != null ? value.data : 0;
1385 value = sa.peekValue(
1386 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1387 info.minResizeWidth = value != null ? value.data : info.minWidth;
1388 value = sa.peekValue(
1389 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1390 info.minResizeHeight = value != null ? value.data : info.minHeight;
1391 info.updatePeriodMillis = sa.getInt(
1392 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1393 info.initialLayout = sa.getResourceId(
1394 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001395 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1396 AppWidgetProviderInfo_initialKeyguardLayout, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001397 String className = sa
1398 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1399 if (className != null) {
1400 info.configure = new ComponentName(component.getPackageName(), className);
1401 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001402 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001403 info.icon = ri.getIconResource();
1404 info.previewImage = sa.getResourceId(
1405 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1406 info.autoAdvanceViewId = sa.getResourceId(
1407 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1408 info.resizeMode = sa.getInt(
1409 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1410 AppWidgetProviderInfo.RESIZE_NONE);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001411 info.widgetCategory = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001412 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001413 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
Amith Yamasani742a6712011-05-04 14:49:28 -07001414
1415 sa.recycle();
1416 } catch (Exception e) {
1417 // Ok to catch Exception here, because anything going wrong because
1418 // of what a client process passes to us should not be fatal for the
1419 // system process.
1420 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1421 return null;
1422 } finally {
1423 if (parser != null)
1424 parser.close();
1425 }
1426 return p;
1427 }
1428
1429 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001430 PackageInfo pkgInfo = null;
1431 try {
1432 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1433 } catch (RemoteException re) {
1434 // Shouldn't happen, local call
1435 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001436 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1437 throw new PackageManager.NameNotFoundException();
1438 }
1439 return pkgInfo.applicationInfo.uid;
1440 }
1441
Jim Millerf229e4d2012-09-12 20:32:50 -07001442 int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1443 int callingUid = Binder.getCallingUid();
Michael Jurka03bdc8a2012-09-21 16:10:21 -07001444 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
Jim Millerf229e4d2012-09-12 20:32:50 -07001445 return callingUid;
1446 }
1447 return enforceCallingUid(packageName);
1448 }
1449
Amith Yamasani742a6712011-05-04 14:49:28 -07001450 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1451 int callingUid = Binder.getCallingUid();
1452 int packageUid;
1453 try {
1454 packageUid = getUidForPackage(packageName);
1455 } catch (PackageManager.NameNotFoundException ex) {
1456 throw new IllegalArgumentException("packageName and uid don't match packageName="
1457 + packageName);
1458 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001459 if (!UserHandle.isSameApp(callingUid, packageUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001460 throw new IllegalArgumentException("packageName and uid don't match packageName="
1461 + packageName);
1462 }
1463 return callingUid;
1464 }
1465
1466 void sendInitialBroadcasts() {
1467 synchronized (mAppWidgetIds) {
1468 ensureStateLoadedLocked();
1469 final int N = mInstalledProviders.size();
1470 for (int i = 0; i < N; i++) {
1471 Provider p = mInstalledProviders.get(i);
1472 if (p.instances.size() > 0) {
1473 sendEnableIntentLocked(p);
1474 int[] appWidgetIds = getAppWidgetIds(p);
1475 sendUpdateIntentLocked(p, appWidgetIds);
1476 registerForBroadcastsLocked(p, appWidgetIds);
1477 }
1478 }
1479 }
1480 }
1481
1482 // only call from initialization -- it assumes that the data structures are all empty
1483 void loadStateLocked() {
1484 AtomicFile file = savedStateFile();
1485 try {
1486 FileInputStream stream = file.openRead();
1487 readStateFromFileLocked(stream);
1488
1489 if (stream != null) {
1490 try {
1491 stream.close();
1492 } catch (IOException e) {
1493 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1494 }
1495 }
1496 } catch (FileNotFoundException e) {
1497 Slog.w(TAG, "Failed to read state: " + e);
1498 }
1499 }
1500
1501 void saveStateLocked() {
1502 AtomicFile file = savedStateFile();
1503 FileOutputStream stream;
1504 try {
1505 stream = file.startWrite();
1506 if (writeStateToFileLocked(stream)) {
1507 file.finishWrite(stream);
1508 } else {
1509 file.failWrite(stream);
1510 Slog.w(TAG, "Failed to save state, restoring backup.");
1511 }
1512 } catch (IOException e) {
1513 Slog.w(TAG, "Failed open state file for write: " + e);
1514 }
1515 }
1516
1517 boolean writeStateToFileLocked(FileOutputStream stream) {
1518 int N;
1519
1520 try {
1521 XmlSerializer out = new FastXmlSerializer();
1522 out.setOutput(stream, "utf-8");
1523 out.startDocument(null, true);
1524 out.startTag(null, "gs");
1525
1526 int providerIndex = 0;
1527 N = mInstalledProviders.size();
1528 for (int i = 0; i < N; i++) {
1529 Provider p = mInstalledProviders.get(i);
1530 if (p.instances.size() > 0) {
1531 out.startTag(null, "p");
1532 out.attribute(null, "pkg", p.info.provider.getPackageName());
1533 out.attribute(null, "cl", p.info.provider.getClassName());
1534 out.endTag(null, "p");
1535 p.tag = providerIndex;
1536 providerIndex++;
1537 }
1538 }
1539
1540 N = mHosts.size();
1541 for (int i = 0; i < N; i++) {
1542 Host host = mHosts.get(i);
1543 out.startTag(null, "h");
1544 out.attribute(null, "pkg", host.packageName);
1545 out.attribute(null, "id", Integer.toHexString(host.hostId));
1546 out.endTag(null, "h");
1547 host.tag = i;
1548 }
1549
1550 N = mAppWidgetIds.size();
1551 for (int i = 0; i < N; i++) {
1552 AppWidgetId id = mAppWidgetIds.get(i);
1553 out.startTag(null, "g");
1554 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1555 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1556 if (id.provider != null) {
1557 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1558 }
Adam Cohen0aa2d422012-09-07 17:37:26 -07001559 if (id.options != null) {
1560 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1561 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1562 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1563 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1564 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1565 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1566 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1567 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1568 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1569 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1570 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001571 out.endTag(null, "g");
1572 }
1573
Michael Jurka61a5b012012-04-13 10:39:45 -07001574 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1575 while (it.hasNext()) {
1576 out.startTag(null, "b");
1577 out.attribute(null, "packageName", it.next());
1578 out.endTag(null, "b");
1579 }
1580
Amith Yamasani742a6712011-05-04 14:49:28 -07001581 out.endTag(null, "gs");
1582
1583 out.endDocument();
1584 return true;
1585 } catch (IOException e) {
1586 Slog.w(TAG, "Failed to write state: " + e);
1587 return false;
1588 }
1589 }
1590
Adam Cohen0aa2d422012-09-07 17:37:26 -07001591 @SuppressWarnings("unused")
Amith Yamasani742a6712011-05-04 14:49:28 -07001592 void readStateFromFileLocked(FileInputStream stream) {
1593 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001594 try {
1595 XmlPullParser parser = Xml.newPullParser();
1596 parser.setInput(stream, null);
1597
1598 int type;
1599 int providerIndex = 0;
1600 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1601 do {
1602 type = parser.next();
1603 if (type == XmlPullParser.START_TAG) {
1604 String tag = parser.getName();
1605 if ("p".equals(tag)) {
1606 // TODO: do we need to check that this package has the same signature
1607 // as before?
1608 String pkg = parser.getAttributeValue(null, "pkg");
1609 String cl = parser.getAttributeValue(null, "cl");
1610
Amith Yamasanif203aee2012-08-29 18:41:53 -07001611 final IPackageManager packageManager = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -07001612 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001613 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
Amith Yamasanif203aee2012-08-29 18:41:53 -07001614 } catch (RemoteException e) {
1615 String[] pkgs = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001616 .currentToCanonicalPackageNames(new String[] { pkg });
1617 pkg = pkgs[0];
1618 }
1619
1620 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1621 if (p == null && mSafeMode) {
1622 // if we're in safe mode, make a temporary one
1623 p = new Provider();
1624 p.info = new AppWidgetProviderInfo();
1625 p.info.provider = new ComponentName(pkg, cl);
1626 p.zombie = true;
1627 mInstalledProviders.add(p);
1628 }
1629 if (p != null) {
1630 // if it wasn't uninstalled or something
1631 loadedProviders.put(providerIndex, p);
1632 }
1633 providerIndex++;
1634 } else if ("h".equals(tag)) {
1635 Host host = new Host();
1636
1637 // TODO: do we need to check that this package has the same signature
1638 // as before?
1639 host.packageName = parser.getAttributeValue(null, "pkg");
1640 try {
1641 host.uid = getUidForPackage(host.packageName);
1642 } catch (PackageManager.NameNotFoundException ex) {
1643 host.zombie = true;
1644 }
1645 if (!host.zombie || mSafeMode) {
1646 // In safe mode, we don't discard the hosts we don't recognize
1647 // so that they're not pruned from our list. Otherwise, we do.
1648 host.hostId = Integer
1649 .parseInt(parser.getAttributeValue(null, "id"), 16);
1650 mHosts.add(host);
1651 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001652 } else if ("b".equals(tag)) {
1653 String packageName = parser.getAttributeValue(null, "packageName");
1654 if (packageName != null) {
1655 mPackagesWithBindWidgetPermission.add(packageName);
1656 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001657 } else if ("g".equals(tag)) {
1658 AppWidgetId id = new AppWidgetId();
1659 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1660 if (id.appWidgetId >= mNextAppWidgetId) {
1661 mNextAppWidgetId = id.appWidgetId + 1;
1662 }
1663
Adam Cohen0aa2d422012-09-07 17:37:26 -07001664 Bundle options = new Bundle();
1665 String minWidthString = parser.getAttributeValue(null, "min_width");
1666 if (minWidthString != null) {
1667 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1668 Integer.parseInt(minWidthString, 16));
1669 }
1670 String minHeightString = parser.getAttributeValue(null, "min_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001671 if (minHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001672 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1673 Integer.parseInt(minHeightString, 16));
1674 }
Adam Cohendb38d8a2012-09-21 18:14:58 -07001675 String maxWidthString = parser.getAttributeValue(null, "max_width");
1676 if (maxWidthString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001677 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1678 Integer.parseInt(maxWidthString, 16));
1679 }
1680 String maxHeightString = parser.getAttributeValue(null, "max_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001681 if (maxHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001682 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1683 Integer.parseInt(maxHeightString, 16));
1684 }
1685 String categoryString = parser.getAttributeValue(null, "host_category");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001686 if (categoryString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001687 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1688 Integer.parseInt(categoryString, 16));
1689 }
1690 id.options = options;
1691
Amith Yamasani742a6712011-05-04 14:49:28 -07001692 String providerString = parser.getAttributeValue(null, "p");
1693 if (providerString != null) {
1694 // there's no provider if it hasn't been bound yet.
1695 // maybe we don't have to save this, but it brings the system
1696 // to the state it was in.
1697 int pIndex = Integer.parseInt(providerString, 16);
1698 id.provider = loadedProviders.get(pIndex);
1699 if (false) {
1700 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1701 + pIndex + " which is " + id.provider);
1702 }
1703 if (id.provider == null) {
1704 // This provider is gone. We just let the host figure out
1705 // that this happened when it fails to load it.
1706 continue;
1707 }
1708 }
1709
1710 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1711 id.host = mHosts.get(hIndex);
1712 if (id.host == null) {
1713 // This host is gone.
1714 continue;
1715 }
1716
1717 if (id.provider != null) {
1718 id.provider.instances.add(id);
1719 }
1720 id.host.instances.add(id);
1721 mAppWidgetIds.add(id);
1722 }
1723 }
1724 } while (type != XmlPullParser.END_DOCUMENT);
1725 success = true;
1726 } catch (NullPointerException e) {
1727 Slog.w(TAG, "failed parsing " + e);
1728 } catch (NumberFormatException e) {
1729 Slog.w(TAG, "failed parsing " + e);
1730 } catch (XmlPullParserException e) {
1731 Slog.w(TAG, "failed parsing " + e);
1732 } catch (IOException e) {
1733 Slog.w(TAG, "failed parsing " + e);
1734 } catch (IndexOutOfBoundsException e) {
1735 Slog.w(TAG, "failed parsing " + e);
1736 }
1737
1738 if (success) {
1739 // delete any hosts that didn't manage to get connected (should happen)
1740 // if it matters, they'll be reconnected.
1741 for (int i = mHosts.size() - 1; i >= 0; i--) {
1742 pruneHostLocked(mHosts.get(i));
1743 }
1744 } else {
1745 // failed reading, clean up
1746 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1747
1748 mAppWidgetIds.clear();
1749 mHosts.clear();
1750 final int N = mInstalledProviders.size();
1751 for (int i = 0; i < N; i++) {
1752 mInstalledProviders.get(i).instances.clear();
1753 }
1754 }
1755 }
1756
Amith Yamasani13593602012-03-22 16:16:17 -07001757 static File getSettingsFile(int userId) {
Amith Yamasani61f57372012-08-31 12:12:28 -07001758 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
Amith Yamasani13593602012-03-22 16:16:17 -07001759 }
1760
Amith Yamasani742a6712011-05-04 14:49:28 -07001761 AtomicFile savedStateFile() {
Amith Yamasani61f57372012-08-31 12:12:28 -07001762 File dir = Environment.getUserSystemDirectory(mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001763 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001764 if (!settingsFile.exists() && mUserId == 0) {
1765 if (!dir.exists()) {
1766 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001767 }
Amith Yamasanie0eb39b2012-05-01 13:48:48 -07001768 // Migrate old data
1769 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1770 // Method doesn't throw an exception on failure. Ignore any errors
1771 // in moving the file (like non-existence)
1772 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001773 }
1774 return new AtomicFile(settingsFile);
1775 }
1776
Amith Yamasani756901d2012-10-12 12:30:07 -07001777 void onUserStopping() {
Amith Yamasani13593602012-03-22 16:16:17 -07001778 // prune the ones we don't want to keep
1779 int N = mInstalledProviders.size();
1780 for (int i = N - 1; i >= 0; i--) {
1781 Provider p = mInstalledProviders.get(i);
1782 cancelBroadcasts(p);
1783 }
Amith Yamasani756901d2012-10-12 12:30:07 -07001784 }
1785
1786 void onUserRemoved() {
Amith Yamasani13593602012-03-22 16:16:17 -07001787 getSettingsFile(mUserId).delete();
1788 }
1789
Winson Chung7fbd2842012-06-13 10:35:51 -07001790 boolean addProvidersForPackageLocked(String pkgName) {
1791 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001792 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1793 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001794 List<ResolveInfo> broadcastReceivers;
1795 try {
1796 broadcastReceivers = mPm.queryIntentReceivers(intent,
1797 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1798 PackageManager.GET_META_DATA, mUserId);
1799 } catch (RemoteException re) {
1800 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001801 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001802 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001803 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1804 for (int i = 0; i < N; i++) {
1805 ResolveInfo ri = broadcastReceivers.get(i);
1806 ActivityInfo ai = ri.activityInfo;
1807 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1808 continue;
1809 }
1810 if (pkgName.equals(ai.packageName)) {
1811 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001812 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001813 }
1814 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001815
1816 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001817 }
1818
Winson Chunga3195052012-06-25 10:02:10 -07001819 /**
1820 * Updates all providers with the specified package names, and records any providers that were
1821 * pruned.
1822 *
1823 * @return whether any providers were updated
1824 */
1825 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001826 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001827 HashSet<String> keep = new HashSet<String>();
1828 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1829 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001830 List<ResolveInfo> broadcastReceivers;
1831 try {
1832 broadcastReceivers = mPm.queryIntentReceivers(intent,
1833 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1834 PackageManager.GET_META_DATA, mUserId);
1835 } catch (RemoteException re) {
1836 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001837 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001838 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001839
1840 // add the missing ones and collect which ones to keep
1841 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1842 for (int i = 0; i < N; i++) {
1843 ResolveInfo ri = broadcastReceivers.get(i);
1844 ActivityInfo ai = ri.activityInfo;
1845 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1846 continue;
1847 }
1848 if (pkgName.equals(ai.packageName)) {
1849 ComponentName component = new ComponentName(ai.packageName, ai.name);
1850 Provider p = lookupProviderLocked(component);
1851 if (p == null) {
1852 if (addProviderLocked(ri)) {
1853 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001854 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001855 }
1856 } else {
1857 Provider parsed = parseProviderInfoXml(component, ri);
1858 if (parsed != null) {
1859 keep.add(ai.name);
1860 // Use the new AppWidgetProviderInfo.
1861 p.info = parsed.info;
1862 // If it's enabled
1863 final int M = p.instances.size();
1864 if (M > 0) {
1865 int[] appWidgetIds = getAppWidgetIds(p);
1866 // Reschedule for the new updatePeriodMillis (don't worry about handling
1867 // it specially if updatePeriodMillis didn't change because we just sent
1868 // an update, and the next one will be updatePeriodMillis from now).
1869 cancelBroadcasts(p);
1870 registerForBroadcastsLocked(p, appWidgetIds);
1871 // If it's currently showing, call back with the new
1872 // AppWidgetProviderInfo.
1873 for (int j = 0; j < M; j++) {
1874 AppWidgetId id = p.instances.get(j);
1875 id.views = null;
1876 if (id.host != null && id.host.callbacks != null) {
1877 try {
1878 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1879 } catch (RemoteException ex) {
1880 // It failed; remove the callback. No need to prune because
1881 // we know that this host is still referenced by this
1882 // instance.
1883 id.host.callbacks = null;
1884 }
1885 }
1886 }
1887 // Now that we've told the host, push out an update.
1888 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07001889 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001890 }
1891 }
1892 }
1893 }
1894 }
1895
1896 // prune the ones we don't want to keep
1897 N = mInstalledProviders.size();
1898 for (int i = N - 1; i >= 0; i--) {
1899 Provider p = mInstalledProviders.get(i);
1900 if (pkgName.equals(p.info.provider.getPackageName())
1901 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07001902 if (removedProviders != null) {
1903 removedProviders.add(p.info.provider);
1904 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001905 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001906 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001907 }
1908 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001909
1910 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07001911 }
1912
Winson Chung7fbd2842012-06-13 10:35:51 -07001913 boolean removeProvidersForPackageLocked(String pkgName) {
1914 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001915 int N = mInstalledProviders.size();
1916 for (int i = N - 1; i >= 0; i--) {
1917 Provider p = mInstalledProviders.get(i);
1918 if (pkgName.equals(p.info.provider.getPackageName())) {
1919 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001920 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001921 }
1922 }
1923
1924 // Delete the hosts for this package too
1925 //
1926 // By now, we have removed any AppWidgets that were in any hosts here,
1927 // so we don't need to worry about sending DISABLE broadcasts to them.
1928 N = mHosts.size();
1929 for (int i = N - 1; i >= 0; i--) {
1930 Host host = mHosts.get(i);
1931 if (pkgName.equals(host.packageName)) {
1932 deleteHostLocked(host);
1933 }
1934 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001935
1936 return providersRemoved;
1937 }
1938
1939 void notifyHostsForProvidersChangedLocked() {
1940 final int N = mHosts.size();
1941 for (int i = N - 1; i >= 0; i--) {
1942 Host host = mHosts.get(i);
1943 try {
1944 if (host.callbacks != null) {
1945 host.callbacks.providersChanged();
1946 }
1947 } catch (RemoteException ex) {
1948 // It failed; remove the callback. No need to prune because
1949 // we know that this host is still referenced by this
1950 // instance.
1951 host.callbacks = null;
1952 }
1953 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001954 }
1955}