blob: fe92b264fef830c042539d065b97d505cb28637a [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;
Adam Cohena1a2f962012-11-01 14:06:16 -070044import android.os.Handler;
45import android.os.HandlerThread;
Amith Yamasani742a6712011-05-04 14:49:28 -070046import android.os.IBinder;
Adam Cohena1a2f962012-11-01 14:06:16 -070047import android.os.Looper;
Jim Millerf229e4d32012-09-12 20:32:50 -070048import android.os.Process;
Amith Yamasani742a6712011-05-04 14:49:28 -070049import android.os.RemoteException;
50import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070051import android.os.UserHandle;
Dianne Hackborn39606a02012-07-31 17:54:35 -070052import android.util.AtomicFile;
Amith Yamasani742a6712011-05-04 14:49:28 -070053import android.util.AttributeSet;
54import android.util.Log;
55import android.util.Pair;
56import android.util.Slog;
57import android.util.TypedValue;
58import android.util.Xml;
Jeff Browna8b9def2012-07-23 14:22:49 -070059import android.view.Display;
Adam Cohen311c79c2012-05-10 14:44:38 -070060import android.view.WindowManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070061import android.widget.RemoteViews;
62
63import com.android.internal.appwidget.IAppWidgetHost;
Amith Yamasani742a6712011-05-04 14:49:28 -070064import com.android.internal.util.FastXmlSerializer;
65import com.android.internal.widget.IRemoteViewsAdapterConnection;
66import com.android.internal.widget.IRemoteViewsFactory;
Amith Yamasani742a6712011-05-04 14:49:28 -070067
68import org.xmlpull.v1.XmlPullParser;
69import org.xmlpull.v1.XmlPullParserException;
70import org.xmlpull.v1.XmlSerializer;
71
72import java.io.File;
73import java.io.FileDescriptor;
74import java.io.FileInputStream;
75import java.io.FileNotFoundException;
76import java.io.FileOutputStream;
77import java.io.IOException;
78import java.io.PrintWriter;
79import java.util.ArrayList;
80import java.util.HashMap;
81import java.util.HashSet;
82import java.util.Iterator;
83import java.util.List;
84import java.util.Locale;
85import java.util.Set;
86
87class AppWidgetServiceImpl {
88
89 private static final String TAG = "AppWidgetServiceImpl";
90 private static final String SETTINGS_FILENAME = "appwidgets.xml";
91 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
92
Amith Yamasani8320de82012-10-05 16:10:38 -070093 private static boolean DBG = false;
94
Amith Yamasani742a6712011-05-04 14:49:28 -070095 /*
96 * When identifying a Host or Provider based on the calling process, use the uid field. When
97 * identifying a Host or Provider based on a package manager broadcast, use the package given.
98 */
99
100 static class Provider {
101 int uid;
102 AppWidgetProviderInfo info;
103 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
104 PendingIntent broadcast;
105 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
106
107 int tag; // for use while saving state (the index)
108 }
109
110 static class Host {
111 int uid;
112 int hostId;
113 String packageName;
114 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
115 IAppWidgetHost callbacks;
116 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
117
118 int tag; // for use while saving state (the index)
119 }
120
121 static class AppWidgetId {
122 int appWidgetId;
123 Provider provider;
124 RemoteViews views;
Adam Cohend2097eb2012-05-01 18:10:28 -0700125 Bundle options;
Amith Yamasani742a6712011-05-04 14:49:28 -0700126 Host host;
127 }
128
129 /**
130 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
131 * needs to be a static inner class since a reference to the ServiceConnection is held globally
132 * and may lead us to leak AppWidgetService instances (if there were more than one).
133 */
134 static class ServiceConnectionProxy implements ServiceConnection {
135 private final IBinder mConnectionCb;
136
137 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
138 mConnectionCb = connectionCb;
139 }
140
141 public void onServiceConnected(ComponentName name, IBinder service) {
142 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
143 .asInterface(mConnectionCb);
144 try {
145 cb.onServiceConnected(service);
146 } catch (Exception e) {
147 e.printStackTrace();
148 }
149 }
150
151 public void onServiceDisconnected(ComponentName name) {
152 disconnect();
153 }
154
155 public void disconnect() {
156 final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
157 .asInterface(mConnectionCb);
158 try {
159 cb.onServiceDisconnected();
160 } catch (Exception e) {
161 e.printStackTrace();
162 }
163 }
164 }
165
166 // Manages active connections to RemoteViewsServices
167 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
168 // Manages persistent references to RemoteViewsServices from different App Widgets
169 private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
170
171 Context mContext;
172 Locale mLocale;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700173 IPackageManager mPm;
Amith Yamasani742a6712011-05-04 14:49:28 -0700174 AlarmManager mAlarmManager;
175 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
176 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
177 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
178 ArrayList<Host> mHosts = new ArrayList<Host>();
Michael Jurka61a5b012012-04-13 10:39:45 -0700179 // set of package names
180 HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
Amith Yamasani742a6712011-05-04 14:49:28 -0700181 boolean mSafeMode;
182 int mUserId;
183 boolean mStateLoaded;
Adam Cohen311c79c2012-05-10 14:44:38 -0700184 int mMaxWidgetBitmapMemory;
Amith Yamasani742a6712011-05-04 14:49:28 -0700185
Adam Cohena1a2f962012-11-01 14:06:16 -0700186 private final Handler mSaveStateHandler;
187
Amith Yamasani742a6712011-05-04 14:49:28 -0700188 // These are for debugging only -- widgets are going missing in some rare instances
189 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
190 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
191
Adam Cohena1a2f962012-11-01 14:06:16 -0700192 AppWidgetServiceImpl(Context context, int userId, Handler saveStateHandler) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700193 mContext = context;
Amith Yamasani483f3b02012-03-13 16:08:00 -0700194 mPm = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -0700195 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
196 mUserId = userId;
Adam Cohena1a2f962012-11-01 14:06:16 -0700197 mSaveStateHandler = saveStateHandler;
Adam Cohen311c79c2012-05-10 14:44:38 -0700198 computeMaximumWidgetBitmapMemory();
199 }
200
201 void computeMaximumWidgetBitmapMemory() {
202 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Jeff Browna8b9def2012-07-23 14:22:49 -0700203 Display display = wm.getDefaultDisplay();
204 Point size = new Point();
205 display.getRealSize(size);
Winson Chunge92aad42012-06-22 14:11:47 -0700206 // Cap memory usage at 1.5 times the size of the display
207 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
Jeff Browna8b9def2012-07-23 14:22:49 -0700208 mMaxWidgetBitmapMemory = 6 * size.x * size.y;
Amith Yamasani742a6712011-05-04 14:49:28 -0700209 }
210
211 public void systemReady(boolean safeMode) {
212 mSafeMode = safeMode;
213
214 synchronized (mAppWidgetIds) {
215 ensureStateLoadedLocked();
216 }
217 }
218
Amith Yamasani8320de82012-10-05 16:10:38 -0700219 private void log(String msg) {
220 Slog.i(TAG, "u=" + mUserId + ": " + msg);
221 }
222
Amith Yamasani742a6712011-05-04 14:49:28 -0700223 void onConfigurationChanged() {
Amith Yamasani8320de82012-10-05 16:10:38 -0700224 if (DBG) log("Got onConfigurationChanged()");
Amith Yamasani742a6712011-05-04 14:49:28 -0700225 Locale revised = Locale.getDefault();
226 if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
227 mLocale = revised;
228
229 synchronized (mAppWidgetIds) {
230 ensureStateLoadedLocked();
Winson Chunga3195052012-06-25 10:02:10 -0700231 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
232 // list of installed providers and skip providers that we don't need to update.
233 // Also note that remove the provider does not clear the Provider component data.
234 ArrayList<Provider> installedProviders =
235 new ArrayList<Provider>(mInstalledProviders);
236 HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
237 int N = installedProviders.size();
Amith Yamasani742a6712011-05-04 14:49:28 -0700238 for (int i = N - 1; i >= 0; i--) {
Winson Chunga3195052012-06-25 10:02:10 -0700239 Provider p = installedProviders.get(i);
240 ComponentName cn = p.info.provider;
241 if (!removedProviders.contains(cn)) {
242 updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
243 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700244 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700245 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700246 }
247 }
248 }
249
250 void onBroadcastReceived(Intent intent) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700251 if (DBG) log("onBroadcast " + intent);
Amith Yamasani742a6712011-05-04 14:49:28 -0700252 final String action = intent.getAction();
253 boolean added = false;
254 boolean changed = false;
Winson Chung7fbd2842012-06-13 10:35:51 -0700255 boolean providersModified = false;
Amith Yamasani742a6712011-05-04 14:49:28 -0700256 String pkgList[] = null;
257 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
258 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
259 added = true;
260 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
261 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
262 added = false;
263 } else {
264 Uri uri = intent.getData();
265 if (uri == null) {
266 return;
267 }
268 String pkgName = uri.getSchemeSpecificPart();
269 if (pkgName == null) {
270 return;
271 }
272 pkgList = new String[] { pkgName };
273 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
274 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
275 }
276 if (pkgList == null || pkgList.length == 0) {
277 return;
278 }
279 if (added || changed) {
280 synchronized (mAppWidgetIds) {
281 ensureStateLoadedLocked();
282 Bundle extras = intent.getExtras();
283 if (changed
284 || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
285 for (String pkgName : pkgList) {
286 // The package was just upgraded
Winson Chunga3195052012-06-25 10:02:10 -0700287 providersModified |= updateProvidersForPackageLocked(pkgName, null);
Amith Yamasani742a6712011-05-04 14:49:28 -0700288 }
289 } else {
290 // The package was just added
291 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700292 providersModified |= addProvidersForPackageLocked(pkgName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700293 }
294 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700295 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700296 }
297 } else {
298 Bundle extras = intent.getExtras();
299 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
300 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
301 } else {
302 synchronized (mAppWidgetIds) {
303 ensureStateLoadedLocked();
304 for (String pkgName : pkgList) {
Winson Chung7fbd2842012-06-13 10:35:51 -0700305 providersModified |= removeProvidersForPackageLocked(pkgName);
Adam Cohena1a2f962012-11-01 14:06:16 -0700306 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700307 }
308 }
309 }
310 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700311
312 if (providersModified) {
313 // If the set of providers has been modified, notify each active AppWidgetHost
314 synchronized (mAppWidgetIds) {
315 ensureStateLoadedLocked();
316 notifyHostsForProvidersChangedLocked();
317 }
318 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700319 }
320
321 private void dumpProvider(Provider p, int index, PrintWriter pw) {
322 AppWidgetProviderInfo info = p.info;
323 pw.print(" ["); pw.print(index); pw.print("] provider ");
324 pw.print(info.provider.flattenToShortString());
325 pw.println(':');
326 pw.print(" min=("); pw.print(info.minWidth);
327 pw.print("x"); pw.print(info.minHeight);
328 pw.print(") minResize=("); pw.print(info.minResizeWidth);
329 pw.print("x"); pw.print(info.minResizeHeight);
330 pw.print(") updatePeriodMillis=");
331 pw.print(info.updatePeriodMillis);
332 pw.print(" resizeMode=");
333 pw.print(info.resizeMode);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700334 pw.print(info.widgetCategory);
Amith Yamasani742a6712011-05-04 14:49:28 -0700335 pw.print(" autoAdvanceViewId=");
336 pw.print(info.autoAdvanceViewId);
337 pw.print(" initialLayout=#");
338 pw.print(Integer.toHexString(info.initialLayout));
339 pw.print(" zombie="); pw.println(p.zombie);
340 }
341
342 private void dumpHost(Host host, int index, PrintWriter pw) {
343 pw.print(" ["); pw.print(index); pw.print("] hostId=");
344 pw.print(host.hostId); pw.print(' ');
345 pw.print(host.packageName); pw.print('/');
346 pw.print(host.uid); pw.println(':');
347 pw.print(" callbacks="); pw.println(host.callbacks);
348 pw.print(" instances.size="); pw.print(host.instances.size());
349 pw.print(" zombie="); pw.println(host.zombie);
350 }
351
352 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
353 pw.print(" ["); pw.print(index); pw.print("] id=");
354 pw.println(id.appWidgetId);
355 pw.print(" hostId=");
356 pw.print(id.host.hostId); pw.print(' ');
357 pw.print(id.host.packageName); pw.print('/');
358 pw.println(id.host.uid);
359 if (id.provider != null) {
360 pw.print(" provider=");
361 pw.println(id.provider.info.provider.flattenToShortString());
362 }
363 if (id.host != null) {
364 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
365 }
366 if (id.views != null) {
367 pw.print(" views="); pw.println(id.views);
368 }
369 }
370
371 void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
372 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
373 != PackageManager.PERMISSION_GRANTED) {
374 pw.println("Permission Denial: can't dump from from pid="
375 + Binder.getCallingPid()
376 + ", uid=" + Binder.getCallingUid());
377 return;
378 }
379
380 synchronized (mAppWidgetIds) {
381 int N = mInstalledProviders.size();
382 pw.println("Providers:");
383 for (int i=0; i<N; i++) {
384 dumpProvider(mInstalledProviders.get(i), i, pw);
385 }
386
387 N = mAppWidgetIds.size();
388 pw.println(" ");
389 pw.println("AppWidgetIds:");
390 for (int i=0; i<N; i++) {
391 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
392 }
393
394 N = mHosts.size();
395 pw.println(" ");
396 pw.println("Hosts:");
397 for (int i=0; i<N; i++) {
398 dumpHost(mHosts.get(i), i, pw);
399 }
400
401 N = mDeletedProviders.size();
402 pw.println(" ");
403 pw.println("Deleted Providers:");
404 for (int i=0; i<N; i++) {
405 dumpProvider(mDeletedProviders.get(i), i, pw);
406 }
407
408 N = mDeletedHosts.size();
409 pw.println(" ");
410 pw.println("Deleted Hosts:");
411 for (int i=0; i<N; i++) {
412 dumpHost(mDeletedHosts.get(i), i, pw);
413 }
414 }
415 }
416
417 private void ensureStateLoadedLocked() {
418 if (!mStateLoaded) {
Adam Cohena1a2f962012-11-01 14:06:16 -0700419 loadAppWidgetListLocked();
Amith Yamasani742a6712011-05-04 14:49:28 -0700420 loadStateLocked();
421 mStateLoaded = true;
422 }
423 }
424
425 public int allocateAppWidgetId(String packageName, int hostId) {
Jim Millerf229e4d32012-09-12 20:32:50 -0700426 int callingUid = enforceSystemOrCallingUid(packageName);
Amith Yamasani742a6712011-05-04 14:49:28 -0700427 synchronized (mAppWidgetIds) {
428 ensureStateLoadedLocked();
429 int appWidgetId = mNextAppWidgetId++;
430
431 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
432
433 AppWidgetId id = new AppWidgetId();
434 id.appWidgetId = appWidgetId;
435 id.host = host;
436
437 host.instances.add(id);
438 mAppWidgetIds.add(id);
439
Adam Cohena1a2f962012-11-01 14:06:16 -0700440 saveStateAsync();
Amith Yamasani8320de82012-10-05 16:10:38 -0700441 if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
442 + " id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700443 return appWidgetId;
444 }
445 }
446
447 public void deleteAppWidgetId(int appWidgetId) {
448 synchronized (mAppWidgetIds) {
449 ensureStateLoadedLocked();
450 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
451 if (id != null) {
452 deleteAppWidgetLocked(id);
Adam Cohena1a2f962012-11-01 14:06:16 -0700453 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700454 }
455 }
456 }
457
458 public void deleteHost(int hostId) {
459 synchronized (mAppWidgetIds) {
460 ensureStateLoadedLocked();
461 int callingUid = Binder.getCallingUid();
462 Host host = lookupHostLocked(callingUid, hostId);
463 if (host != null) {
464 deleteHostLocked(host);
Adam Cohena1a2f962012-11-01 14:06:16 -0700465 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700466 }
467 }
468 }
469
470 public void deleteAllHosts() {
471 synchronized (mAppWidgetIds) {
472 ensureStateLoadedLocked();
473 int callingUid = Binder.getCallingUid();
474 final int N = mHosts.size();
475 boolean changed = false;
476 for (int i = N - 1; i >= 0; i--) {
477 Host host = mHosts.get(i);
478 if (host.uid == callingUid) {
479 deleteHostLocked(host);
480 changed = true;
481 }
482 }
483 if (changed) {
Adam Cohena1a2f962012-11-01 14:06:16 -0700484 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700485 }
486 }
487 }
488
489 void deleteHostLocked(Host host) {
490 final int N = host.instances.size();
491 for (int i = N - 1; i >= 0; i--) {
492 AppWidgetId id = host.instances.get(i);
493 deleteAppWidgetLocked(id);
494 }
495 host.instances.clear();
496 mHosts.remove(host);
497 mDeletedHosts.add(host);
498 // it's gone or going away, abruptly drop the callback connection
499 host.callbacks = null;
500 }
501
502 void deleteAppWidgetLocked(AppWidgetId id) {
503 // We first unbind all services that are bound to this id
504 unbindAppWidgetRemoteViewsServicesLocked(id);
505
506 Host host = id.host;
507 host.instances.remove(id);
508 pruneHostLocked(host);
509
510 mAppWidgetIds.remove(id);
511
512 Provider p = id.provider;
513 if (p != null) {
514 p.instances.remove(id);
515 if (!p.zombie) {
516 // send the broacast saying that this appWidgetId has been deleted
517 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
518 intent.setComponent(p.info.provider);
519 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700520 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700521 if (p.instances.size() == 0) {
522 // cancel the future updates
523 cancelBroadcasts(p);
524
525 // send the broacast saying that the provider is not in use any more
526 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
527 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700528 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -0700529 }
530 }
531 }
532 }
533
534 void cancelBroadcasts(Provider p) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700535 if (DBG) log("cancelBroadcasts for " + p);
Amith Yamasani742a6712011-05-04 14:49:28 -0700536 if (p.broadcast != null) {
537 mAlarmManager.cancel(p.broadcast);
538 long token = Binder.clearCallingIdentity();
539 try {
540 p.broadcast.cancel();
541 } finally {
542 Binder.restoreCallingIdentity(token);
543 }
544 p.broadcast = null;
545 }
546 }
547
Adam Cohen0aa2d422012-09-07 17:37:26 -0700548 private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700549 if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
550 + " provider=" + provider);
Amith Yamasani742a6712011-05-04 14:49:28 -0700551 final long ident = Binder.clearCallingIdentity();
552 try {
553 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700554 options = cloneIfLocalBinder(options);
Amith Yamasani742a6712011-05-04 14:49:28 -0700555 ensureStateLoadedLocked();
556 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
557 if (id == null) {
558 throw new IllegalArgumentException("bad appWidgetId");
559 }
560 if (id.provider != null) {
561 throw new IllegalArgumentException("appWidgetId " + appWidgetId
562 + " already bound to " + id.provider.info.provider);
563 }
564 Provider p = lookupProviderLocked(provider);
565 if (p == null) {
566 throw new IllegalArgumentException("not a appwidget provider: " + provider);
567 }
568 if (p.zombie) {
569 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
570 + " safe mode: " + provider);
571 }
572
Amith Yamasani742a6712011-05-04 14:49:28 -0700573 id.provider = p;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700574 if (options == null) {
575 options = new Bundle();
576 }
577 id.options = options;
578
579 // We need to provide a default value for the widget category if it is not specified
580 if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
581 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
582 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
583 }
584
Amith Yamasani742a6712011-05-04 14:49:28 -0700585 p.instances.add(id);
586 int instancesSize = p.instances.size();
587 if (instancesSize == 1) {
588 // tell the provider that it's ready
589 sendEnableIntentLocked(p);
590 }
591
592 // send an update now -- We need this update now, and just for this appWidgetId.
593 // It's less critical when the next one happens, so when we schedule the next one,
594 // we add updatePeriodMillis to its start time. That time will have some slop,
595 // but that's okay.
596 sendUpdateIntentLocked(p, new int[] { appWidgetId });
597
598 // schedule the future updates
599 registerForBroadcastsLocked(p, getAppWidgetIds(p));
Adam Cohena1a2f962012-11-01 14:06:16 -0700600 saveStateAsync();
Amith Yamasani742a6712011-05-04 14:49:28 -0700601 }
602 } finally {
603 Binder.restoreCallingIdentity(ident);
604 }
605 }
606
Adam Cohen0aa2d422012-09-07 17:37:26 -0700607 public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka67a871d2012-11-01 18:26:01 -0700608 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET,
Michael Jurka61a5b012012-04-13 10:39:45 -0700609 "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700610 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700611 }
612
613 public boolean bindAppWidgetIdIfAllowed(
Adam Cohen0aa2d422012-09-07 17:37:26 -0700614 String packageName, int appWidgetId, ComponentName provider, Bundle options) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700615 try {
Michael Jurka67a871d2012-11-01 18:26:01 -0700616 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
Michael Jurka61a5b012012-04-13 10:39:45 -0700617 } catch (SecurityException se) {
618 if (!callerHasBindAppWidgetPermission(packageName)) {
619 return false;
620 }
621 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700622 bindAppWidgetIdImpl(appWidgetId, provider, options);
Michael Jurka61a5b012012-04-13 10:39:45 -0700623 return true;
624 }
625
626 private boolean callerHasBindAppWidgetPermission(String packageName) {
627 int callingUid = Binder.getCallingUid();
628 try {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700629 if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
Michael Jurka61a5b012012-04-13 10:39:45 -0700630 return false;
631 }
632 } catch (Exception e) {
633 return false;
634 }
635 synchronized (mAppWidgetIds) {
636 ensureStateLoadedLocked();
637 return mPackagesWithBindWidgetPermission.contains(packageName);
638 }
639 }
640
641 public boolean hasBindAppWidgetPermission(String packageName) {
642 mContext.enforceCallingPermission(
643 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
644 "hasBindAppWidgetPermission packageName=" + packageName);
645
646 synchronized (mAppWidgetIds) {
647 ensureStateLoadedLocked();
648 return mPackagesWithBindWidgetPermission.contains(packageName);
649 }
650 }
651
652 public void setBindAppWidgetPermission(String packageName, boolean permission) {
653 mContext.enforceCallingPermission(
654 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
655 "setBindAppWidgetPermission packageName=" + packageName);
656
657 synchronized (mAppWidgetIds) {
658 ensureStateLoadedLocked();
659 if (permission) {
660 mPackagesWithBindWidgetPermission.add(packageName);
661 } else {
662 mPackagesWithBindWidgetPermission.remove(packageName);
663 }
Adam Cohena1a2f962012-11-01 14:06:16 -0700664 saveStateAsync();
Michael Jurka61a5b012012-04-13 10:39:45 -0700665 }
Michael Jurka61a5b012012-04-13 10:39:45 -0700666 }
667
Amith Yamasani742a6712011-05-04 14:49:28 -0700668 // Binds to a specific RemoteViewsService
669 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
670 synchronized (mAppWidgetIds) {
671 ensureStateLoadedLocked();
672 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
673 if (id == null) {
674 throw new IllegalArgumentException("bad appWidgetId");
675 }
676 final ComponentName componentName = intent.getComponent();
677 try {
Amith Yamasani98edc952012-09-25 14:09:27 -0700678 final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
679 PackageManager.GET_PERMISSIONS, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700680 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
681 throw new SecurityException("Selected service does not require "
682 + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
683 }
Amith Yamasani98edc952012-09-25 14:09:27 -0700684 } catch (RemoteException e) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700685 throw new IllegalArgumentException("Unknown component " + componentName);
686 }
687
688 // If there is already a connection made for this service intent, then disconnect from
689 // that first. (This does not allow multiple connections to the same service under
690 // the same key)
691 ServiceConnectionProxy conn = null;
692 FilterComparison fc = new FilterComparison(intent);
693 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
694 if (mBoundRemoteViewsServices.containsKey(key)) {
695 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
696 conn.disconnect();
697 mContext.unbindService(conn);
698 mBoundRemoteViewsServices.remove(key);
699 }
700
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700701 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700702 // Bind to the RemoteViewsService (which will trigger a callback to the
703 // RemoteViewsAdapter.onServiceConnected())
704 final long token = Binder.clearCallingIdentity();
705 try {
706 conn = new ServiceConnectionProxy(key, connection);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800707 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700708 mBoundRemoteViewsServices.put(key, conn);
709 } finally {
710 Binder.restoreCallingIdentity(token);
711 }
712
713 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
714 // when we can call back to the RemoteViewsService later to destroy associated
715 // factories.
716 incrementAppWidgetServiceRefCount(appWidgetId, fc);
717 }
718 }
719
720 // Unbinds from a specific RemoteViewsService
721 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
722 synchronized (mAppWidgetIds) {
723 ensureStateLoadedLocked();
724 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
725 // RemoteViewsAdapter)
726 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
727 intent));
728 if (mBoundRemoteViewsServices.containsKey(key)) {
729 // We don't need to use the appWidgetId until after we are sure there is something
730 // to unbind. Note that this may mask certain issues with apps calling unbind()
731 // more than necessary.
732 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
733 if (id == null) {
734 throw new IllegalArgumentException("bad appWidgetId");
735 }
736
737 ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
738 .get(key);
739 conn.disconnect();
740 mContext.unbindService(conn);
741 mBoundRemoteViewsServices.remove(key);
742 } else {
743 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
744 }
745 }
746 }
747
748 // Unbinds from a RemoteViewsService when we delete an app widget
749 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
750 int appWidgetId = id.appWidgetId;
751 // Unbind all connections to Services bound to this AppWidgetId
752 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
753 .iterator();
754 while (it.hasNext()) {
755 final Pair<Integer, Intent.FilterComparison> key = it.next();
756 if (key.first.intValue() == appWidgetId) {
757 final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
758 .get(key);
759 conn.disconnect();
760 mContext.unbindService(conn);
761 it.remove();
762 }
763 }
764
765 // Check if we need to destroy any services (if no other app widgets are
766 // referencing the same service)
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800767 decrementAppWidgetServiceRefCount(id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700768 }
769
770 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800771 private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700772 final ServiceConnection conn = new ServiceConnection() {
773 @Override
774 public void onServiceConnected(ComponentName name, IBinder service) {
775 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
776 try {
777 cb.onDestroy(intent);
778 } catch (RemoteException e) {
779 e.printStackTrace();
780 } catch (RuntimeException e) {
781 e.printStackTrace();
782 }
783 mContext.unbindService(this);
784 }
785
786 @Override
787 public void onServiceDisconnected(android.content.ComponentName name) {
788 // Do nothing
789 }
790 };
791
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700792 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -0700793 // Bind to the service and remove the static intent->factory mapping in the
794 // RemoteViewsService.
795 final long token = Binder.clearCallingIdentity();
796 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800797 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700798 } finally {
799 Binder.restoreCallingIdentity(token);
800 }
801 }
802
803 // Adds to the ref-count for a given RemoteViewsService intent
804 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
805 HashSet<Integer> appWidgetIds = null;
806 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
807 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
808 } else {
809 appWidgetIds = new HashSet<Integer>();
810 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
811 }
812 appWidgetIds.add(appWidgetId);
813 }
814
815 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
816 // the ref-count reaches zero.
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800817 private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700818 Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
819 while (it.hasNext()) {
820 final FilterComparison key = it.next();
821 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800822 if (ids.remove(id.appWidgetId)) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700823 // If we have removed the last app widget referencing this service, then we
824 // should destroy it and remove it from this set
825 if (ids.isEmpty()) {
Amith Yamasani37ce3a82012-02-06 12:04:42 -0800826 destroyRemoteViewsService(key.getIntent(), id);
Amith Yamasani742a6712011-05-04 14:49:28 -0700827 it.remove();
828 }
829 }
830 }
831 }
832
833 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
834 synchronized (mAppWidgetIds) {
835 ensureStateLoadedLocked();
836 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
837 if (id != null && id.provider != null && !id.provider.zombie) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700838 return cloneIfLocalBinder(id.provider.info);
Amith Yamasani742a6712011-05-04 14:49:28 -0700839 }
840 return null;
841 }
842 }
843
844 public RemoteViews getAppWidgetViews(int appWidgetId) {
Amith Yamasani8320de82012-10-05 16:10:38 -0700845 if (DBG) log("getAppWidgetViews id=" + appWidgetId);
Amith Yamasani742a6712011-05-04 14:49:28 -0700846 synchronized (mAppWidgetIds) {
847 ensureStateLoadedLocked();
848 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
849 if (id != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700850 return cloneIfLocalBinder(id.views);
Amith Yamasani742a6712011-05-04 14:49:28 -0700851 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700852 if (DBG) log(" couldn't find appwidgetid");
Amith Yamasani742a6712011-05-04 14:49:28 -0700853 return null;
854 }
855 }
856
857 public List<AppWidgetProviderInfo> getInstalledProviders() {
Adam Cohen3fcc6b22012-11-15 14:10:07 -0800858 return getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
859 }
860
861 private List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700862 synchronized (mAppWidgetIds) {
863 ensureStateLoadedLocked();
864 final int N = mInstalledProviders.size();
865 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
866 for (int i = 0; i < N; i++) {
867 Provider p = mInstalledProviders.get(i);
Adam Cohen3fcc6b22012-11-15 14:10:07 -0800868 if (!p.zombie && (p.info.widgetCategory & categoryFilter) != 0) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700869 result.add(cloneIfLocalBinder(p.info));
Amith Yamasani742a6712011-05-04 14:49:28 -0700870 }
871 }
872 return result;
873 }
874 }
875
876 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
877 if (appWidgetIds == null) {
878 return;
879 }
Amith Yamasani8320de82012-10-05 16:10:38 -0700880 if (DBG) log("updateAppWidgetIds views: " + views);
Adam Cohenf08a8b72012-07-16 12:02:10 -0700881 int bitmapMemoryUsage = 0;
882 if (views != null) {
883 bitmapMemoryUsage = views.estimateMemoryUsage();
884 }
Adam Cohen311c79c2012-05-10 14:44:38 -0700885 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
886 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
887 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
888 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
889 " fill the device's screen once.");
890 }
891
Amith Yamasani742a6712011-05-04 14:49:28 -0700892 if (appWidgetIds.length == 0) {
893 return;
894 }
895 final int N = appWidgetIds.length;
896
897 synchronized (mAppWidgetIds) {
898 ensureStateLoadedLocked();
899 for (int i = 0; i < N; i++) {
900 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
901 updateAppWidgetInstanceLocked(id, views);
902 }
903 }
904 }
905
Adam Cohena1a2f962012-11-01 14:06:16 -0700906 private void saveStateAsync() {
907 mSaveStateHandler.post(mSaveStateRunnable);
908 }
909
910 private final Runnable mSaveStateRunnable = new Runnable() {
911 @Override
912 public void run() {
913 synchronized (mAppWidgetIds) {
914 ensureStateLoadedLocked();
915 saveStateLocked();
916 }
917 }
918 };
919
Adam Cohend2097eb2012-05-01 18:10:28 -0700920 public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
Adam Cohene8724c82012-04-19 17:11:40 -0700921 synchronized (mAppWidgetIds) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700922 options = cloneIfLocalBinder(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700923 ensureStateLoadedLocked();
924 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
925
926 if (id == null) {
927 return;
928 }
Adam Cohen0aa2d422012-09-07 17:37:26 -0700929
Adam Cohene8724c82012-04-19 17:11:40 -0700930 Provider p = id.provider;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700931 // Merge the options
932 id.options.putAll(options);
Adam Cohene8724c82012-04-19 17:11:40 -0700933
934 // send the broacast saying that this appWidgetId has been deleted
Adam Cohend2097eb2012-05-01 18:10:28 -0700935 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
Adam Cohene8724c82012-04-19 17:11:40 -0700936 intent.setComponent(p.info.provider);
937 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700938 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700939 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Adam Cohena1a2f962012-11-01 14:06:16 -0700940 saveStateAsync();
Adam Cohene8724c82012-04-19 17:11:40 -0700941 }
942 }
943
Adam Cohend2097eb2012-05-01 18:10:28 -0700944 public Bundle getAppWidgetOptions(int appWidgetId) {
Adam Cohene8724c82012-04-19 17:11:40 -0700945 synchronized (mAppWidgetIds) {
946 ensureStateLoadedLocked();
947 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
Adam Cohend2097eb2012-05-01 18:10:28 -0700948 if (id != null && id.options != null) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700949 return cloneIfLocalBinder(id.options);
Adam Cohene8724c82012-04-19 17:11:40 -0700950 } else {
951 return Bundle.EMPTY;
952 }
953 }
954 }
955
Amith Yamasani742a6712011-05-04 14:49:28 -0700956 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
957 if (appWidgetIds == null) {
958 return;
959 }
960 if (appWidgetIds.length == 0) {
961 return;
962 }
963 final int N = appWidgetIds.length;
964
965 synchronized (mAppWidgetIds) {
966 ensureStateLoadedLocked();
967 for (int i = 0; i < N; i++) {
968 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung66119882012-10-11 14:26:25 -0700969 if (id.views != null) {
970 // Only trigger a partial update for a widget if it has received a full update
971 updateAppWidgetInstanceLocked(id, views, true);
972 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700973 }
974 }
975 }
976
977 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
978 if (appWidgetIds == null) {
979 return;
980 }
981 if (appWidgetIds.length == 0) {
982 return;
983 }
984 final int N = appWidgetIds.length;
985
986 synchronized (mAppWidgetIds) {
987 ensureStateLoadedLocked();
988 for (int i = 0; i < N; i++) {
989 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
990 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
991 }
992 }
993 }
994
995 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
996 synchronized (mAppWidgetIds) {
997 ensureStateLoadedLocked();
998 Provider p = lookupProviderLocked(provider);
999 if (p == null) {
1000 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
1001 return;
1002 }
1003 ArrayList<AppWidgetId> instances = p.instances;
1004 final int callingUid = Binder.getCallingUid();
1005 final int N = instances.size();
1006 for (int i = 0; i < N; i++) {
1007 AppWidgetId id = instances.get(i);
1008 if (canAccessAppWidgetId(id, callingUid)) {
1009 updateAppWidgetInstanceLocked(id, views);
1010 }
1011 }
1012 }
1013 }
1014
1015 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
1016 updateAppWidgetInstanceLocked(id, views, false);
1017 }
1018
1019 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
1020 // allow for stale appWidgetIds and other badness
1021 // lookup also checks that the calling process can access the appWidgetId
1022 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1023 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1024
Winson Chung66119882012-10-11 14:26:25 -07001025 if (!isPartialUpdate) {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001026 // For a full update we replace the RemoteViews completely.
Amith Yamasani742a6712011-05-04 14:49:28 -07001027 id.views = views;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001028 } else {
1029 // For a partial update, we merge the new RemoteViews with the old.
1030 id.views.mergeRemoteViews(views);
1031 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001032
1033 // is anyone listening?
1034 if (id.host.callbacks != null) {
1035 try {
1036 // the lock is held, but this is a oneway call
1037 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
1038 } catch (RemoteException e) {
1039 // It failed; remove the callback. No need to prune because
1040 // we know that this host is still referenced by this instance.
1041 id.host.callbacks = null;
1042 }
1043 }
1044 }
1045 }
1046
1047 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1048 // allow for stale appWidgetIds and other badness
1049 // lookup also checks that the calling process can access the appWidgetId
1050 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1051 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1052 // is anyone listening?
1053 if (id.host.callbacks != null) {
1054 try {
1055 // the lock is held, but this is a oneway call
1056 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1057 } catch (RemoteException e) {
1058 // It failed; remove the callback. No need to prune because
1059 // we know that this host is still referenced by this instance.
1060 id.host.callbacks = null;
1061 }
1062 }
1063
1064 // If the host is unavailable, then we call the associated
1065 // RemoteViewsFactory.onDataSetChanged() directly
1066 if (id.host.callbacks == null) {
1067 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1068 for (FilterComparison key : keys) {
1069 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1070 Intent intent = key.getIntent();
1071
1072 final ServiceConnection conn = new ServiceConnection() {
1073 @Override
1074 public void onServiceConnected(ComponentName name, IBinder service) {
1075 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1076 .asInterface(service);
1077 try {
1078 cb.onDataSetChangedAsync();
1079 } catch (RemoteException e) {
1080 e.printStackTrace();
1081 } catch (RuntimeException e) {
1082 e.printStackTrace();
1083 }
1084 mContext.unbindService(this);
1085 }
1086
1087 @Override
1088 public void onServiceDisconnected(android.content.ComponentName name) {
1089 // Do nothing
1090 }
1091 };
1092
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001093 int userId = UserHandle.getUserId(id.provider.uid);
Amith Yamasani742a6712011-05-04 14:49:28 -07001094 // Bind to the service and call onDataSetChanged()
1095 final long token = Binder.clearCallingIdentity();
1096 try {
Amith Yamasani37ce3a82012-02-06 12:04:42 -08001097 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001098 } finally {
1099 Binder.restoreCallingIdentity(token);
1100 }
1101 }
1102 }
1103 }
1104 }
1105 }
1106
Adam Cohen3ff2d862012-09-26 14:07:57 -07001107 private boolean isLocalBinder() {
1108 return Process.myPid() == Binder.getCallingPid();
1109 }
1110
1111 private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1112 if (isLocalBinder() && rv != null) {
1113 return rv.clone();
1114 }
1115 return rv;
1116 }
1117
1118 private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
1119 if (isLocalBinder() && info != null) {
1120 return info.clone();
1121 }
1122 return info;
1123 }
1124
1125 private Bundle cloneIfLocalBinder(Bundle bundle) {
1126 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
1127 // if we start adding objects to the options. Further, it would only be an issue if keyguard
1128 // used such options.
1129 if (isLocalBinder() && bundle != null) {
1130 return (Bundle) bundle.clone();
1131 }
1132 return bundle;
1133 }
1134
Amith Yamasani742a6712011-05-04 14:49:28 -07001135 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1136 List<RemoteViews> updatedViews) {
1137 int callingUid = enforceCallingUid(packageName);
1138 synchronized (mAppWidgetIds) {
1139 ensureStateLoadedLocked();
1140 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1141 host.callbacks = callbacks;
1142
1143 updatedViews.clear();
1144
1145 ArrayList<AppWidgetId> instances = host.instances;
1146 int N = instances.size();
1147 int[] updatedIds = new int[N];
1148 for (int i = 0; i < N; i++) {
1149 AppWidgetId id = instances.get(i);
1150 updatedIds[i] = id.appWidgetId;
Adam Cohen3ff2d862012-09-26 14:07:57 -07001151 updatedViews.add(cloneIfLocalBinder(id.views));
Amith Yamasani742a6712011-05-04 14:49:28 -07001152 }
1153 return updatedIds;
1154 }
1155 }
1156
1157 public void stopListening(int hostId) {
1158 synchronized (mAppWidgetIds) {
1159 ensureStateLoadedLocked();
1160 Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1161 if (host != null) {
1162 host.callbacks = null;
1163 pruneHostLocked(host);
1164 }
1165 }
1166 }
1167
1168 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1169 if (id.host.uid == callingUid) {
1170 // Apps hosting the AppWidget have access to it.
1171 return true;
1172 }
1173 if (id.provider != null && id.provider.uid == callingUid) {
1174 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1175 return true;
1176 }
1177 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1178 // Apps that can bind have access to all appWidgetIds.
1179 return true;
1180 }
1181 // Nobody else can access it.
1182 return false;
1183 }
1184
1185 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1186 int callingUid = Binder.getCallingUid();
1187 final int N = mAppWidgetIds.size();
1188 for (int i = 0; i < N; i++) {
1189 AppWidgetId id = mAppWidgetIds.get(i);
1190 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1191 return id;
1192 }
1193 }
1194 return null;
1195 }
1196
1197 Provider lookupProviderLocked(ComponentName provider) {
1198 final int N = mInstalledProviders.size();
1199 for (int i = 0; i < N; i++) {
1200 Provider p = mInstalledProviders.get(i);
1201 if (p.info.provider.equals(provider)) {
1202 return p;
1203 }
1204 }
1205 return null;
1206 }
1207
1208 Host lookupHostLocked(int uid, int hostId) {
1209 final int N = mHosts.size();
1210 for (int i = 0; i < N; i++) {
1211 Host h = mHosts.get(i);
1212 if (h.uid == uid && h.hostId == hostId) {
1213 return h;
1214 }
1215 }
1216 return null;
1217 }
1218
1219 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1220 final int N = mHosts.size();
1221 for (int i = 0; i < N; i++) {
1222 Host h = mHosts.get(i);
1223 if (h.hostId == hostId && h.packageName.equals(packageName)) {
1224 return h;
1225 }
1226 }
1227 Host host = new Host();
1228 host.packageName = packageName;
1229 host.uid = uid;
1230 host.hostId = hostId;
1231 mHosts.add(host);
1232 return host;
1233 }
1234
1235 void pruneHostLocked(Host host) {
1236 if (host.instances.size() == 0 && host.callbacks == null) {
1237 mHosts.remove(host);
1238 }
1239 }
1240
Adam Cohena1a2f962012-11-01 14:06:16 -07001241 void loadAppWidgetListLocked() {
Amith Yamasani742a6712011-05-04 14:49:28 -07001242 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001243 try {
1244 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1245 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1246 PackageManager.GET_META_DATA, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001247
Amith Yamasani483f3b02012-03-13 16:08:00 -07001248 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1249 for (int i = 0; i < N; i++) {
1250 ResolveInfo ri = broadcastReceivers.get(i);
1251 addProviderLocked(ri);
1252 }
1253 } catch (RemoteException re) {
1254 // Shouldn't happen, local call
Amith Yamasani742a6712011-05-04 14:49:28 -07001255 }
1256 }
1257
1258 boolean addProviderLocked(ResolveInfo ri) {
1259 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1260 return false;
1261 }
1262 if (!ri.activityInfo.isEnabled()) {
1263 return false;
1264 }
1265 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1266 ri.activityInfo.name), ri);
1267 if (p != null) {
1268 mInstalledProviders.add(p);
1269 return true;
1270 } else {
1271 return false;
1272 }
1273 }
1274
1275 void removeProviderLocked(int index, Provider p) {
1276 int N = p.instances.size();
1277 for (int i = 0; i < N; i++) {
1278 AppWidgetId id = p.instances.get(i);
1279 // Call back with empty RemoteViews
1280 updateAppWidgetInstanceLocked(id, null);
1281 // Stop telling the host about updates for this from now on
1282 cancelBroadcasts(p);
1283 // clear out references to this appWidgetId
1284 id.host.instances.remove(id);
1285 mAppWidgetIds.remove(id);
1286 id.provider = null;
1287 pruneHostLocked(id.host);
1288 id.host = null;
1289 }
1290 p.instances.clear();
1291 mInstalledProviders.remove(index);
1292 mDeletedProviders.add(p);
1293 // no need to send the DISABLE broadcast, since the receiver is gone anyway
1294 cancelBroadcasts(p);
1295 }
1296
1297 void sendEnableIntentLocked(Provider p) {
1298 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1299 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001300 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001301 }
1302
1303 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1304 if (appWidgetIds != null && appWidgetIds.length > 0) {
1305 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1306 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1307 intent.setComponent(p.info.provider);
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001308 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001309 }
1310 }
1311
1312 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1313 if (p.info.updatePeriodMillis > 0) {
1314 // if this is the first instance, set the alarm. otherwise,
1315 // rely on the fact that we've already set it and that
1316 // PendingIntent.getBroadcast will update the extras.
1317 boolean alreadyRegistered = p.broadcast != null;
1318 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1319 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1320 intent.setComponent(p.info.provider);
1321 long token = Binder.clearCallingIdentity();
1322 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001323 p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
1324 PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
Amith Yamasani742a6712011-05-04 14:49:28 -07001325 } finally {
1326 Binder.restoreCallingIdentity(token);
1327 }
1328 if (!alreadyRegistered) {
1329 long period = p.info.updatePeriodMillis;
1330 if (period < MIN_UPDATE_PERIOD) {
1331 period = MIN_UPDATE_PERIOD;
1332 }
1333 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1334 .elapsedRealtime()
1335 + period, period, p.broadcast);
1336 }
1337 }
1338 }
1339
1340 static int[] getAppWidgetIds(Provider p) {
1341 int instancesSize = p.instances.size();
1342 int appWidgetIds[] = new int[instancesSize];
1343 for (int i = 0; i < instancesSize; i++) {
1344 appWidgetIds[i] = p.instances.get(i).appWidgetId;
1345 }
1346 return appWidgetIds;
1347 }
1348
1349 public int[] getAppWidgetIds(ComponentName provider) {
1350 synchronized (mAppWidgetIds) {
1351 ensureStateLoadedLocked();
1352 Provider p = lookupProviderLocked(provider);
1353 if (p != null && Binder.getCallingUid() == p.uid) {
1354 return getAppWidgetIds(p);
1355 } else {
1356 return new int[0];
1357 }
1358 }
1359 }
1360
Michael Jurka75b5cfb2012-11-15 18:22:47 -08001361 static int[] getAppWidgetIds(Host h) {
1362 int instancesSize = h.instances.size();
1363 int appWidgetIds[] = new int[instancesSize];
1364 for (int i = 0; i < instancesSize; i++) {
1365 appWidgetIds[i] = h.instances.get(i).appWidgetId;
1366 }
1367 return appWidgetIds;
1368 }
1369
1370 public int[] getAppWidgetIdsForHost(int hostId) {
1371 synchronized (mAppWidgetIds) {
1372 ensureStateLoadedLocked();
1373 int callingUid = Binder.getCallingUid();
1374 Host host = lookupHostLocked(callingUid, hostId);
1375 if (host != null) {
1376 return getAppWidgetIds(host);
1377 } else {
1378 return new int[0];
1379 }
1380 }
1381 }
1382
Amith Yamasani742a6712011-05-04 14:49:28 -07001383 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1384 Provider p = null;
1385
1386 ActivityInfo activityInfo = ri.activityInfo;
1387 XmlResourceParser parser = null;
1388 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001389 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
Amith Yamasani742a6712011-05-04 14:49:28 -07001390 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1391 if (parser == null) {
1392 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1393 + " meta-data for " + "AppWidget provider '" + component + '\'');
1394 return null;
1395 }
1396
1397 AttributeSet attrs = Xml.asAttributeSet(parser);
1398
1399 int type;
1400 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1401 && type != XmlPullParser.START_TAG) {
1402 // drain whitespace, comments, etc.
1403 }
1404
1405 String nodeName = parser.getName();
1406 if (!"appwidget-provider".equals(nodeName)) {
1407 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1408 + " AppWidget provider '" + component + '\'');
1409 return null;
1410 }
1411
1412 p = new Provider();
1413 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1414 info.provider = component;
1415 p.uid = activityInfo.applicationInfo.uid;
1416
Amith Yamasani483f3b02012-03-13 16:08:00 -07001417 Resources res = mContext.getPackageManager()
Amith Yamasani8320de82012-10-05 16:10:38 -07001418 .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);
Amith Yamasani742a6712011-05-04 14:49:28 -07001419
1420 TypedArray sa = res.obtainAttributes(attrs,
1421 com.android.internal.R.styleable.AppWidgetProviderInfo);
1422
1423 // These dimensions has to be resolved in the application's context.
1424 // We simply send back the raw complex data, which will be
1425 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1426 TypedValue value = sa
1427 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1428 info.minWidth = value != null ? value.data : 0;
1429 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1430 info.minHeight = value != null ? value.data : 0;
1431 value = sa.peekValue(
1432 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1433 info.minResizeWidth = value != null ? value.data : info.minWidth;
1434 value = sa.peekValue(
1435 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1436 info.minResizeHeight = value != null ? value.data : info.minHeight;
1437 info.updatePeriodMillis = sa.getInt(
1438 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1439 info.initialLayout = sa.getResourceId(
1440 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001441 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1442 AppWidgetProviderInfo_initialKeyguardLayout, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001443 String className = sa
1444 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1445 if (className != null) {
1446 info.configure = new ComponentName(component.getPackageName(), className);
1447 }
Amith Yamasani483f3b02012-03-13 16:08:00 -07001448 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
Amith Yamasani742a6712011-05-04 14:49:28 -07001449 info.icon = ri.getIconResource();
1450 info.previewImage = sa.getResourceId(
1451 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1452 info.autoAdvanceViewId = sa.getResourceId(
1453 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1454 info.resizeMode = sa.getInt(
1455 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1456 AppWidgetProviderInfo.RESIZE_NONE);
Adam Cohen0aa2d422012-09-07 17:37:26 -07001457 info.widgetCategory = sa.getInt(
Michael Jurkaca5e3412012-09-14 12:18:51 -07001458 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
Adam Cohen0aa2d422012-09-07 17:37:26 -07001459 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
Amith Yamasani742a6712011-05-04 14:49:28 -07001460
1461 sa.recycle();
1462 } catch (Exception e) {
1463 // Ok to catch Exception here, because anything going wrong because
1464 // of what a client process passes to us should not be fatal for the
1465 // system process.
1466 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1467 return null;
1468 } finally {
1469 if (parser != null)
1470 parser.close();
1471 }
1472 return p;
1473 }
1474
1475 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
Amith Yamasani483f3b02012-03-13 16:08:00 -07001476 PackageInfo pkgInfo = null;
1477 try {
1478 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1479 } catch (RemoteException re) {
1480 // Shouldn't happen, local call
1481 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001482 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1483 throw new PackageManager.NameNotFoundException();
1484 }
1485 return pkgInfo.applicationInfo.uid;
1486 }
1487
Jim Millerf229e4d32012-09-12 20:32:50 -07001488 int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1489 int callingUid = Binder.getCallingUid();
Michael Jurka03bdc8a2012-09-21 16:10:21 -07001490 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
Jim Millerf229e4d32012-09-12 20:32:50 -07001491 return callingUid;
1492 }
1493 return enforceCallingUid(packageName);
1494 }
1495
Amith Yamasani742a6712011-05-04 14:49:28 -07001496 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1497 int callingUid = Binder.getCallingUid();
1498 int packageUid;
1499 try {
1500 packageUid = getUidForPackage(packageName);
1501 } catch (PackageManager.NameNotFoundException ex) {
1502 throw new IllegalArgumentException("packageName and uid don't match packageName="
1503 + packageName);
1504 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001505 if (!UserHandle.isSameApp(callingUid, packageUid)) {
Amith Yamasani742a6712011-05-04 14:49:28 -07001506 throw new IllegalArgumentException("packageName and uid don't match packageName="
1507 + packageName);
1508 }
1509 return callingUid;
1510 }
1511
1512 void sendInitialBroadcasts() {
1513 synchronized (mAppWidgetIds) {
1514 ensureStateLoadedLocked();
1515 final int N = mInstalledProviders.size();
1516 for (int i = 0; i < N; i++) {
1517 Provider p = mInstalledProviders.get(i);
1518 if (p.instances.size() > 0) {
1519 sendEnableIntentLocked(p);
1520 int[] appWidgetIds = getAppWidgetIds(p);
1521 sendUpdateIntentLocked(p, appWidgetIds);
1522 registerForBroadcastsLocked(p, appWidgetIds);
1523 }
1524 }
1525 }
1526 }
1527
1528 // only call from initialization -- it assumes that the data structures are all empty
1529 void loadStateLocked() {
1530 AtomicFile file = savedStateFile();
1531 try {
1532 FileInputStream stream = file.openRead();
1533 readStateFromFileLocked(stream);
1534
1535 if (stream != null) {
1536 try {
1537 stream.close();
1538 } catch (IOException e) {
1539 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1540 }
1541 }
1542 } catch (FileNotFoundException e) {
1543 Slog.w(TAG, "Failed to read state: " + e);
1544 }
1545 }
1546
1547 void saveStateLocked() {
1548 AtomicFile file = savedStateFile();
1549 FileOutputStream stream;
1550 try {
1551 stream = file.startWrite();
1552 if (writeStateToFileLocked(stream)) {
1553 file.finishWrite(stream);
1554 } else {
1555 file.failWrite(stream);
1556 Slog.w(TAG, "Failed to save state, restoring backup.");
1557 }
1558 } catch (IOException e) {
1559 Slog.w(TAG, "Failed open state file for write: " + e);
1560 }
1561 }
1562
1563 boolean writeStateToFileLocked(FileOutputStream stream) {
1564 int N;
1565
1566 try {
1567 XmlSerializer out = new FastXmlSerializer();
1568 out.setOutput(stream, "utf-8");
1569 out.startDocument(null, true);
1570 out.startTag(null, "gs");
1571
1572 int providerIndex = 0;
1573 N = mInstalledProviders.size();
1574 for (int i = 0; i < N; i++) {
1575 Provider p = mInstalledProviders.get(i);
1576 if (p.instances.size() > 0) {
1577 out.startTag(null, "p");
1578 out.attribute(null, "pkg", p.info.provider.getPackageName());
1579 out.attribute(null, "cl", p.info.provider.getClassName());
1580 out.endTag(null, "p");
1581 p.tag = providerIndex;
1582 providerIndex++;
1583 }
1584 }
1585
1586 N = mHosts.size();
1587 for (int i = 0; i < N; i++) {
1588 Host host = mHosts.get(i);
1589 out.startTag(null, "h");
1590 out.attribute(null, "pkg", host.packageName);
1591 out.attribute(null, "id", Integer.toHexString(host.hostId));
1592 out.endTag(null, "h");
1593 host.tag = i;
1594 }
1595
1596 N = mAppWidgetIds.size();
1597 for (int i = 0; i < N; i++) {
1598 AppWidgetId id = mAppWidgetIds.get(i);
1599 out.startTag(null, "g");
1600 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1601 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1602 if (id.provider != null) {
1603 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1604 }
Adam Cohen0aa2d422012-09-07 17:37:26 -07001605 if (id.options != null) {
1606 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1607 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1608 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1609 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1610 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1611 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1612 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1613 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1614 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1615 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1616 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001617 out.endTag(null, "g");
1618 }
1619
Michael Jurka61a5b012012-04-13 10:39:45 -07001620 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1621 while (it.hasNext()) {
1622 out.startTag(null, "b");
1623 out.attribute(null, "packageName", it.next());
1624 out.endTag(null, "b");
1625 }
1626
Amith Yamasani742a6712011-05-04 14:49:28 -07001627 out.endTag(null, "gs");
1628
1629 out.endDocument();
1630 return true;
1631 } catch (IOException e) {
1632 Slog.w(TAG, "Failed to write state: " + e);
1633 return false;
1634 }
1635 }
1636
Adam Cohen0aa2d422012-09-07 17:37:26 -07001637 @SuppressWarnings("unused")
Amith Yamasani742a6712011-05-04 14:49:28 -07001638 void readStateFromFileLocked(FileInputStream stream) {
1639 boolean success = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001640 try {
1641 XmlPullParser parser = Xml.newPullParser();
1642 parser.setInput(stream, null);
1643
1644 int type;
1645 int providerIndex = 0;
1646 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1647 do {
1648 type = parser.next();
1649 if (type == XmlPullParser.START_TAG) {
1650 String tag = parser.getName();
1651 if ("p".equals(tag)) {
1652 // TODO: do we need to check that this package has the same signature
1653 // as before?
1654 String pkg = parser.getAttributeValue(null, "pkg");
1655 String cl = parser.getAttributeValue(null, "cl");
1656
Amith Yamasanif203aee2012-08-29 18:41:53 -07001657 final IPackageManager packageManager = AppGlobals.getPackageManager();
Amith Yamasani742a6712011-05-04 14:49:28 -07001658 try {
Amith Yamasani8320de82012-10-05 16:10:38 -07001659 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
Amith Yamasanif203aee2012-08-29 18:41:53 -07001660 } catch (RemoteException e) {
1661 String[] pkgs = mContext.getPackageManager()
Amith Yamasani742a6712011-05-04 14:49:28 -07001662 .currentToCanonicalPackageNames(new String[] { pkg });
1663 pkg = pkgs[0];
1664 }
1665
1666 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1667 if (p == null && mSafeMode) {
1668 // if we're in safe mode, make a temporary one
1669 p = new Provider();
1670 p.info = new AppWidgetProviderInfo();
1671 p.info.provider = new ComponentName(pkg, cl);
1672 p.zombie = true;
1673 mInstalledProviders.add(p);
1674 }
1675 if (p != null) {
1676 // if it wasn't uninstalled or something
1677 loadedProviders.put(providerIndex, p);
1678 }
1679 providerIndex++;
1680 } else if ("h".equals(tag)) {
1681 Host host = new Host();
1682
1683 // TODO: do we need to check that this package has the same signature
1684 // as before?
1685 host.packageName = parser.getAttributeValue(null, "pkg");
1686 try {
1687 host.uid = getUidForPackage(host.packageName);
1688 } catch (PackageManager.NameNotFoundException ex) {
1689 host.zombie = true;
1690 }
1691 if (!host.zombie || mSafeMode) {
1692 // In safe mode, we don't discard the hosts we don't recognize
1693 // so that they're not pruned from our list. Otherwise, we do.
1694 host.hostId = Integer
1695 .parseInt(parser.getAttributeValue(null, "id"), 16);
1696 mHosts.add(host);
1697 }
Michael Jurka61a5b012012-04-13 10:39:45 -07001698 } else if ("b".equals(tag)) {
1699 String packageName = parser.getAttributeValue(null, "packageName");
1700 if (packageName != null) {
1701 mPackagesWithBindWidgetPermission.add(packageName);
1702 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001703 } else if ("g".equals(tag)) {
1704 AppWidgetId id = new AppWidgetId();
1705 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1706 if (id.appWidgetId >= mNextAppWidgetId) {
1707 mNextAppWidgetId = id.appWidgetId + 1;
1708 }
1709
Adam Cohen0aa2d422012-09-07 17:37:26 -07001710 Bundle options = new Bundle();
1711 String minWidthString = parser.getAttributeValue(null, "min_width");
1712 if (minWidthString != null) {
1713 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1714 Integer.parseInt(minWidthString, 16));
1715 }
1716 String minHeightString = parser.getAttributeValue(null, "min_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001717 if (minHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001718 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1719 Integer.parseInt(minHeightString, 16));
1720 }
Adam Cohendb38d8a2012-09-21 18:14:58 -07001721 String maxWidthString = parser.getAttributeValue(null, "max_width");
1722 if (maxWidthString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001723 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1724 Integer.parseInt(maxWidthString, 16));
1725 }
1726 String maxHeightString = parser.getAttributeValue(null, "max_height");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001727 if (maxHeightString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001728 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1729 Integer.parseInt(maxHeightString, 16));
1730 }
1731 String categoryString = parser.getAttributeValue(null, "host_category");
Adam Cohendb38d8a2012-09-21 18:14:58 -07001732 if (categoryString != null) {
Adam Cohen0aa2d422012-09-07 17:37:26 -07001733 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1734 Integer.parseInt(categoryString, 16));
1735 }
1736 id.options = options;
1737
Amith Yamasani742a6712011-05-04 14:49:28 -07001738 String providerString = parser.getAttributeValue(null, "p");
1739 if (providerString != null) {
1740 // there's no provider if it hasn't been bound yet.
1741 // maybe we don't have to save this, but it brings the system
1742 // to the state it was in.
1743 int pIndex = Integer.parseInt(providerString, 16);
1744 id.provider = loadedProviders.get(pIndex);
1745 if (false) {
1746 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1747 + pIndex + " which is " + id.provider);
1748 }
1749 if (id.provider == null) {
1750 // This provider is gone. We just let the host figure out
1751 // that this happened when it fails to load it.
1752 continue;
1753 }
1754 }
1755
1756 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1757 id.host = mHosts.get(hIndex);
1758 if (id.host == null) {
1759 // This host is gone.
1760 continue;
1761 }
1762
1763 if (id.provider != null) {
1764 id.provider.instances.add(id);
1765 }
1766 id.host.instances.add(id);
1767 mAppWidgetIds.add(id);
1768 }
1769 }
1770 } while (type != XmlPullParser.END_DOCUMENT);
1771 success = true;
1772 } catch (NullPointerException e) {
1773 Slog.w(TAG, "failed parsing " + e);
1774 } catch (NumberFormatException e) {
1775 Slog.w(TAG, "failed parsing " + e);
1776 } catch (XmlPullParserException e) {
1777 Slog.w(TAG, "failed parsing " + e);
1778 } catch (IOException e) {
1779 Slog.w(TAG, "failed parsing " + e);
1780 } catch (IndexOutOfBoundsException e) {
1781 Slog.w(TAG, "failed parsing " + e);
1782 }
1783
1784 if (success) {
1785 // delete any hosts that didn't manage to get connected (should happen)
1786 // if it matters, they'll be reconnected.
1787 for (int i = mHosts.size() - 1; i >= 0; i--) {
1788 pruneHostLocked(mHosts.get(i));
1789 }
1790 } else {
1791 // failed reading, clean up
1792 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1793
1794 mAppWidgetIds.clear();
1795 mHosts.clear();
1796 final int N = mInstalledProviders.size();
1797 for (int i = 0; i < N; i++) {
1798 mInstalledProviders.get(i).instances.clear();
1799 }
1800 }
1801 }
1802
Amith Yamasani13593602012-03-22 16:16:17 -07001803 static File getSettingsFile(int userId) {
Amith Yamasani61f57372012-08-31 12:12:28 -07001804 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
Amith Yamasani13593602012-03-22 16:16:17 -07001805 }
1806
Amith Yamasani742a6712011-05-04 14:49:28 -07001807 AtomicFile savedStateFile() {
Amith Yamasani61f57372012-08-31 12:12:28 -07001808 File dir = Environment.getUserSystemDirectory(mUserId);
Amith Yamasani13593602012-03-22 16:16:17 -07001809 File settingsFile = getSettingsFile(mUserId);
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001810 if (!settingsFile.exists() && mUserId == 0) {
1811 if (!dir.exists()) {
1812 dir.mkdirs();
Amith Yamasani742a6712011-05-04 14:49:28 -07001813 }
Amith Yamasanie0eb39b52012-05-01 13:48:48 -07001814 // Migrate old data
1815 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1816 // Method doesn't throw an exception on failure. Ignore any errors
1817 // in moving the file (like non-existence)
1818 oldFile.renameTo(settingsFile);
Amith Yamasani742a6712011-05-04 14:49:28 -07001819 }
1820 return new AtomicFile(settingsFile);
1821 }
1822
Amith Yamasani756901d2012-10-12 12:30:07 -07001823 void onUserStopping() {
Amith Yamasani13593602012-03-22 16:16:17 -07001824 // prune the ones we don't want to keep
1825 int N = mInstalledProviders.size();
1826 for (int i = N - 1; i >= 0; i--) {
1827 Provider p = mInstalledProviders.get(i);
1828 cancelBroadcasts(p);
1829 }
Amith Yamasani756901d2012-10-12 12:30:07 -07001830 }
1831
1832 void onUserRemoved() {
Amith Yamasani13593602012-03-22 16:16:17 -07001833 getSettingsFile(mUserId).delete();
1834 }
1835
Winson Chung7fbd2842012-06-13 10:35:51 -07001836 boolean addProvidersForPackageLocked(String pkgName) {
1837 boolean providersAdded = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001838 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1839 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001840 List<ResolveInfo> broadcastReceivers;
1841 try {
1842 broadcastReceivers = mPm.queryIntentReceivers(intent,
1843 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1844 PackageManager.GET_META_DATA, mUserId);
1845 } catch (RemoteException re) {
1846 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001847 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001848 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001849 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1850 for (int i = 0; i < N; i++) {
1851 ResolveInfo ri = broadcastReceivers.get(i);
1852 ActivityInfo ai = ri.activityInfo;
1853 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1854 continue;
1855 }
1856 if (pkgName.equals(ai.packageName)) {
1857 addProviderLocked(ri);
Winson Chung7fbd2842012-06-13 10:35:51 -07001858 providersAdded = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001859 }
1860 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001861
1862 return providersAdded;
Amith Yamasani742a6712011-05-04 14:49:28 -07001863 }
1864
Winson Chunga3195052012-06-25 10:02:10 -07001865 /**
1866 * Updates all providers with the specified package names, and records any providers that were
1867 * pruned.
1868 *
1869 * @return whether any providers were updated
1870 */
1871 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
Winson Chung7fbd2842012-06-13 10:35:51 -07001872 boolean providersUpdated = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001873 HashSet<String> keep = new HashSet<String>();
1874 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1875 intent.setPackage(pkgName);
Amith Yamasani483f3b02012-03-13 16:08:00 -07001876 List<ResolveInfo> broadcastReceivers;
1877 try {
1878 broadcastReceivers = mPm.queryIntentReceivers(intent,
1879 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1880 PackageManager.GET_META_DATA, mUserId);
1881 } catch (RemoteException re) {
1882 // Shouldn't happen, local call
Winson Chung7fbd2842012-06-13 10:35:51 -07001883 return false;
Amith Yamasani483f3b02012-03-13 16:08:00 -07001884 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001885
1886 // add the missing ones and collect which ones to keep
1887 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1888 for (int i = 0; i < N; i++) {
1889 ResolveInfo ri = broadcastReceivers.get(i);
1890 ActivityInfo ai = ri.activityInfo;
1891 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1892 continue;
1893 }
1894 if (pkgName.equals(ai.packageName)) {
1895 ComponentName component = new ComponentName(ai.packageName, ai.name);
1896 Provider p = lookupProviderLocked(component);
1897 if (p == null) {
1898 if (addProviderLocked(ri)) {
1899 keep.add(ai.name);
Winson Chung7fbd2842012-06-13 10:35:51 -07001900 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001901 }
1902 } else {
1903 Provider parsed = parseProviderInfoXml(component, ri);
1904 if (parsed != null) {
1905 keep.add(ai.name);
1906 // Use the new AppWidgetProviderInfo.
1907 p.info = parsed.info;
1908 // If it's enabled
1909 final int M = p.instances.size();
1910 if (M > 0) {
1911 int[] appWidgetIds = getAppWidgetIds(p);
1912 // Reschedule for the new updatePeriodMillis (don't worry about handling
1913 // it specially if updatePeriodMillis didn't change because we just sent
1914 // an update, and the next one will be updatePeriodMillis from now).
1915 cancelBroadcasts(p);
1916 registerForBroadcastsLocked(p, appWidgetIds);
1917 // If it's currently showing, call back with the new
1918 // AppWidgetProviderInfo.
1919 for (int j = 0; j < M; j++) {
1920 AppWidgetId id = p.instances.get(j);
1921 id.views = null;
1922 if (id.host != null && id.host.callbacks != null) {
1923 try {
1924 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1925 } catch (RemoteException ex) {
1926 // It failed; remove the callback. No need to prune because
1927 // we know that this host is still referenced by this
1928 // instance.
1929 id.host.callbacks = null;
1930 }
1931 }
1932 }
1933 // Now that we've told the host, push out an update.
1934 sendUpdateIntentLocked(p, appWidgetIds);
Winson Chung7fbd2842012-06-13 10:35:51 -07001935 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001936 }
1937 }
1938 }
1939 }
1940 }
1941
1942 // prune the ones we don't want to keep
1943 N = mInstalledProviders.size();
1944 for (int i = N - 1; i >= 0; i--) {
1945 Provider p = mInstalledProviders.get(i);
1946 if (pkgName.equals(p.info.provider.getPackageName())
1947 && !keep.contains(p.info.provider.getClassName())) {
Winson Chunga3195052012-06-25 10:02:10 -07001948 if (removedProviders != null) {
1949 removedProviders.add(p.info.provider);
1950 }
Amith Yamasani742a6712011-05-04 14:49:28 -07001951 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001952 providersUpdated = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001953 }
1954 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001955
1956 return providersUpdated;
Amith Yamasani742a6712011-05-04 14:49:28 -07001957 }
1958
Winson Chung7fbd2842012-06-13 10:35:51 -07001959 boolean removeProvidersForPackageLocked(String pkgName) {
1960 boolean providersRemoved = false;
Amith Yamasani742a6712011-05-04 14:49:28 -07001961 int N = mInstalledProviders.size();
1962 for (int i = N - 1; i >= 0; i--) {
1963 Provider p = mInstalledProviders.get(i);
1964 if (pkgName.equals(p.info.provider.getPackageName())) {
1965 removeProviderLocked(i, p);
Winson Chung7fbd2842012-06-13 10:35:51 -07001966 providersRemoved = true;
Amith Yamasani742a6712011-05-04 14:49:28 -07001967 }
1968 }
1969
1970 // Delete the hosts for this package too
1971 //
1972 // By now, we have removed any AppWidgets that were in any hosts here,
1973 // so we don't need to worry about sending DISABLE broadcasts to them.
1974 N = mHosts.size();
1975 for (int i = N - 1; i >= 0; i--) {
1976 Host host = mHosts.get(i);
1977 if (pkgName.equals(host.packageName)) {
1978 deleteHostLocked(host);
1979 }
1980 }
Winson Chung7fbd2842012-06-13 10:35:51 -07001981
1982 return providersRemoved;
1983 }
1984
1985 void notifyHostsForProvidersChangedLocked() {
1986 final int N = mHosts.size();
1987 for (int i = N - 1; i >= 0; i--) {
1988 Host host = mHosts.get(i);
1989 try {
1990 if (host.callbacks != null) {
1991 host.callbacks.providersChanged();
1992 }
1993 } catch (RemoteException ex) {
1994 // It failed; remove the callback. No need to prune because
1995 // we know that this host is still referenced by this
1996 // instance.
1997 host.callbacks = null;
1998 }
1999 }
Amith Yamasani742a6712011-05-04 14:49:28 -07002000 }
2001}