blob: 2af51038dd214fa8d7da1c7ef48f552f295569d1 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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;
20import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070021import android.appwidget.AppWidgetManager;
22import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
Winson Chung81f39eb2011-01-11 18:05:01 -080028import android.content.ServiceConnection;
Adam Cohen97300312011-10-12 15:48:13 -070029import android.content.Intent.FilterComparison;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.pm.ActivityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -040031import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.pm.PackageInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080033import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.pm.ResolveInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080035import android.content.pm.ServiceInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080036import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.content.res.TypedArray;
38import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.net.Uri;
40import android.os.Binder;
41import android.os.Bundle;
Winson Chung81f39eb2011-01-11 18:05:01 -080042import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.RemoteException;
44import android.os.SystemClock;
45import android.util.AttributeSet;
Winson Chung16c8d8a2011-01-20 16:19:33 -080046import android.util.Log;
Winson Chung81f39eb2011-01-11 18:05:01 -080047import android.util.Pair;
Joe Onorato8a9b2202010-02-26 18:56:32 -080048import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070049import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.util.Xml;
51import android.widget.RemoteViews;
52
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070053import com.android.internal.appwidget.IAppWidgetHost;
Winson Chung81f39eb2011-01-11 18:05:01 -080054import com.android.internal.appwidget.IAppWidgetService;
Adam Cohen97300312011-10-12 15:48:13 -070055import com.android.internal.os.AtomicFile;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080056import com.android.internal.util.FastXmlSerializer;
Winson Chung81f39eb2011-01-11 18:05:01 -080057import com.android.internal.widget.IRemoteViewsAdapterConnection;
Winson Chung84bbb022011-02-21 13:57:45 -080058import com.android.internal.widget.IRemoteViewsFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
Adam Cohen97300312011-10-12 15:48:13 -070060import org.xmlpull.v1.XmlPullParser;
61import org.xmlpull.v1.XmlPullParserException;
62import org.xmlpull.v1.XmlSerializer;
63
64import java.io.File;
65import java.io.FileDescriptor;
66import java.io.FileInputStream;
67import java.io.FileNotFoundException;
68import java.io.FileOutputStream;
69import java.io.IOException;
70import java.io.PrintWriter;
71import java.util.ArrayList;
72import java.util.HashMap;
73import java.util.HashSet;
74import java.util.Iterator;
75import java.util.List;
76import java.util.Locale;
77
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070078class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070080 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070082 private static final String SETTINGS_FILENAME = "appwidgets.xml";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070083 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
85 /*
86 * When identifying a Host or Provider based on the calling process, use the uid field.
87 * When identifying a Host or Provider based on a package manager broadcast, use the
88 * package given.
89 */
90
91 static class Provider {
92 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070093 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070094 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 PendingIntent broadcast;
96 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
97
98 int tag; // for use while saving state (the index)
99 }
100
101 static class Host {
102 int uid;
103 int hostId;
104 String packageName;
Romain Guya5475592009-07-01 17:20:08 -0700105 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700106 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
108
109 int tag; // for use while saving state (the index)
110 }
111
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700112 static class AppWidgetId {
113 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 Provider provider;
115 RemoteViews views;
116 Host host;
117 }
118
Winson Chung81f39eb2011-01-11 18:05:01 -0800119 /**
120 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
121 * This needs to be a static inner class since a reference to the ServiceConnection is held
122 * globally and may lead us to leak AppWidgetService instances (if there were more than one).
123 */
124 static class ServiceConnectionProxy implements ServiceConnection {
Winson Chung81f39eb2011-01-11 18:05:01 -0800125 private final Pair<Integer, Intent.FilterComparison> mKey;
126 private final IBinder mConnectionCb;
127
Winson Chung16c8d8a2011-01-20 16:19:33 -0800128 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800129 mKey = key;
130 mConnectionCb = connectionCb;
131 }
132 public void onServiceConnected(ComponentName name, IBinder service) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800133 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800134 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
135 try {
136 cb.onServiceConnected(service);
Adam Cohenc2be22c2011-03-16 16:33:53 -0700137 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800138 e.printStackTrace();
139 }
140 }
141 public void onServiceDisconnected(ComponentName name) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800142 disconnect();
143 }
144 public void disconnect() {
145 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800146 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
147 try {
148 cb.onServiceDisconnected();
Adam Cohenc2be22c2011-03-16 16:33:53 -0700149 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800150 e.printStackTrace();
151 }
152 }
153 }
154
Winson Chung84bbb022011-02-21 13:57:45 -0800155 // Manages active connections to RemoteViewsServices
Winson Chung81f39eb2011-01-11 18:05:01 -0800156 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
157 mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
Winson Chung84bbb022011-02-21 13:57:45 -0800158 // Manages persistent references to RemoteViewsServices from different App Widgets
159 private final HashMap<FilterComparison, HashSet<Integer>>
160 mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
Winson Chung81f39eb2011-01-11 18:05:01 -0800161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700163 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 PackageManager mPackageManager;
165 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700166 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700167 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700168 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
169 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 boolean mSafeMode;
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700171 boolean mStateLoaded;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172
Adam Cohen7bb98832011-10-05 18:10:13 -0700173 // These are for debugging only -- widgets are going missing in some rare instances
174 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
175 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
176
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700177 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 mContext = context;
179 mPackageManager = context.getPackageManager();
180 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
181 }
182
183 public void systemReady(boolean safeMode) {
184 mSafeMode = safeMode;
185
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700186 synchronized (mAppWidgetIds) {
187 ensureStateLoadedLocked();
188 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189
190 // Register for the boot completed broadcast, so we can send the
191 // ENABLE broacasts. If we try to send them now, they time out,
192 // because the system isn't ready to handle them yet.
193 mContext.registerReceiver(mBroadcastReceiver,
194 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
195
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700196 // Register for configuration changes so we can update the names
197 // of the widgets when the locale changes.
198 mContext.registerReceiver(mBroadcastReceiver,
199 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 // Register for broadcasts about package install, etc., so we can
202 // update the provider list.
203 IntentFilter filter = new IntentFilter();
204 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
Joe Onoratod070e892011-01-07 20:50:37 -0800205 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
207 filter.addDataScheme("package");
208 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800209 // Register for events related to sdcard installation.
210 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800211 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
212 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800213 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 }
215
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700216 private void ensureStateLoadedLocked() {
217 if (!mStateLoaded) {
218 loadAppWidgetList();
219 loadStateLocked();
220 mStateLoaded = true;
221 }
222 }
223
Adam Cohen7bb98832011-10-05 18:10:13 -0700224 private void dumpProvider(Provider p, int index, PrintWriter pw) {
225 AppWidgetProviderInfo info = p.info;
226 pw.print(" ["); pw.print(index); pw.print("] provider ");
227 pw.print(info.provider.flattenToShortString());
228 pw.println(':');
229 pw.print(" min=("); pw.print(info.minWidth);
230 pw.print("x"); pw.print(info.minHeight);
231 pw.print(") minResize=("); pw.print(info.minResizeWidth);
232 pw.print("x"); pw.print(info.minResizeHeight);
233 pw.print(") updatePeriodMillis=");
234 pw.print(info.updatePeriodMillis);
235 pw.print(" resizeMode=");
236 pw.print(info.resizeMode);
237 pw.print(" autoAdvanceViewId=");
238 pw.print(info.autoAdvanceViewId);
239 pw.print(" initialLayout=#");
240 pw.print(Integer.toHexString(info.initialLayout));
241 pw.print(" zombie="); pw.println(p.zombie);
242 }
243
244 private void dumpHost(Host host, int index, PrintWriter pw) {
245 pw.print(" ["); pw.print(index); pw.print("] hostId=");
246 pw.print(host.hostId); pw.print(' ');
247 pw.print(host.packageName); pw.print('/');
248 pw.print(host.uid); pw.println(':');
249 pw.print(" callbacks="); pw.println(host.callbacks);
250 pw.print(" instances.size="); pw.print(host.instances.size());
251 pw.print(" zombie="); pw.println(host.zombie);
252 }
253
254 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
255 pw.print(" ["); pw.print(index); pw.print("] id=");
256 pw.println(id.appWidgetId);
257 pw.print(" hostId=");
258 pw.print(id.host.hostId); pw.print(' ');
259 pw.print(id.host.packageName); pw.print('/');
260 pw.println(id.host.uid);
261 if (id.provider != null) {
262 pw.print(" provider=");
263 pw.println(id.provider.info.provider.flattenToShortString());
264 }
265 if (id.host != null) {
266 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
267 }
268 if (id.views != null) {
269 pw.print(" views="); pw.println(id.views);
270 }
271 }
272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 @Override
274 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
275 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
276 != PackageManager.PERMISSION_GRANTED) {
277 pw.println("Permission Denial: can't dump from from pid="
278 + Binder.getCallingPid()
279 + ", uid=" + Binder.getCallingUid());
280 return;
281 }
282
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700283 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700285 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700287 dumpProvider(mInstalledProviders.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 }
289
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700290 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700291 pw.println(" ");
292 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700294 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 }
296
297 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700298 pw.println(" ");
299 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700301 dumpHost(mHosts.get(i), i, pw);
302 }
303
304 N = mDeletedProviders.size();
305 pw.println(" ");
306 pw.println("Deleted Providers:");
307 for (int i=0; i<N; i++) {
308 dumpProvider(mDeletedProviders.get(i), i, pw);
309 }
310
311 N = mDeletedHosts.size();
312 pw.println(" ");
313 pw.println("Deleted Hosts:");
314 for (int i=0; i<N; i++) {
315 dumpHost(mDeletedHosts.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 }
317 }
318 }
319
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700320 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700322 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700323 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700324 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325
326 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
327
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700328 AppWidgetId id = new AppWidgetId();
329 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 id.host = host;
331
332 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700333 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334
335 saveStateLocked();
336
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700337 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 }
339 }
340
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700341 public void deleteAppWidgetId(int appWidgetId) {
342 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700343 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700344 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700346 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 saveStateLocked();
348 }
349 }
350 }
351
352 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700353 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700354 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 int callingUid = getCallingUid();
356 Host host = lookupHostLocked(callingUid, hostId);
357 if (host != null) {
358 deleteHostLocked(host);
359 saveStateLocked();
360 }
361 }
362 }
363
364 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700365 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700366 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 int callingUid = getCallingUid();
368 final int N = mHosts.size();
369 boolean changed = false;
370 for (int i=N-1; i>=0; i--) {
371 Host host = mHosts.get(i);
372 if (host.uid == callingUid) {
373 deleteHostLocked(host);
374 changed = true;
375 }
376 }
377 if (changed) {
378 saveStateLocked();
379 }
380 }
381 }
382
383 void deleteHostLocked(Host host) {
384 final int N = host.instances.size();
385 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700386 AppWidgetId id = host.instances.get(i);
387 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 }
389 host.instances.clear();
390 mHosts.remove(host);
Adam Cohen7bb98832011-10-05 18:10:13 -0700391 mDeletedHosts.add(host);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 // it's gone or going away, abruptly drop the callback connection
393 host.callbacks = null;
394 }
395
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700396 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800397 // We first unbind all services that are bound to this id
398 unbindAppWidgetRemoteViewsServicesLocked(id);
399
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 Host host = id.host;
401 host.instances.remove(id);
402 pruneHostLocked(host);
403
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700404 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405
406 Provider p = id.provider;
407 if (p != null) {
408 p.instances.remove(id);
409 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700410 // send the broacast saying that this appWidgetId has been deleted
411 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700413 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 mContext.sendBroadcast(intent);
415 if (p.instances.size() == 0) {
416 // cancel the future updates
417 cancelBroadcasts(p);
418
419 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700420 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 intent.setComponent(p.info.provider);
422 mContext.sendBroadcast(intent);
423 }
424 }
425 }
426 }
427
428 void cancelBroadcasts(Provider p) {
429 if (p.broadcast != null) {
430 mAlarmManager.cancel(p.broadcast);
431 long token = Binder.clearCallingIdentity();
432 try {
433 p.broadcast.cancel();
434 } finally {
435 Binder.restoreCallingIdentity(token);
436 }
437 p.broadcast = null;
438 }
439 }
440
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700441 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
442 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
443 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
Dianne Hackborn4912f162011-06-15 17:40:44 -0700444
445 final long ident = Binder.clearCallingIdentity();
446 try {
447 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700448 ensureStateLoadedLocked();
Dianne Hackborn4912f162011-06-15 17:40:44 -0700449 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
450 if (id == null) {
451 throw new IllegalArgumentException("bad appWidgetId");
452 }
453 if (id.provider != null) {
454 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
455 + id.provider.info.provider);
456 }
457 Provider p = lookupProviderLocked(provider);
458 if (p == null) {
459 throw new IllegalArgumentException("not a appwidget provider: " + provider);
460 }
461 if (p.zombie) {
462 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
463 + " safe mode: " + provider);
464 }
465
466 id.provider = p;
467 p.instances.add(id);
468 int instancesSize = p.instances.size();
469 if (instancesSize == 1) {
470 // tell the provider that it's ready
471 sendEnableIntentLocked(p);
472 }
473
474 // send an update now -- We need this update now, and just for this appWidgetId.
475 // It's less critical when the next one happens, so when we schdule the next one,
476 // we add updatePeriodMillis to its start time. That time will have some slop,
477 // but that's okay.
478 sendUpdateIntentLocked(p, new int[] { appWidgetId });
479
480 // schedule the future updates
481 registerForBroadcastsLocked(p, getAppWidgetIds(p));
482 saveStateLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 }
Dianne Hackborn4912f162011-06-15 17:40:44 -0700484 } finally {
485 Binder.restoreCallingIdentity(ident);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
487 }
488
Winson Chung84bbb022011-02-21 13:57:45 -0800489 // Binds to a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800490 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
491 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700492 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800493 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
494 if (id == null) {
495 throw new IllegalArgumentException("bad appWidgetId");
496 }
497 final ComponentName componentName = intent.getComponent();
498 try {
499 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
500 PackageManager.GET_PERMISSIONS);
501 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
502 throw new SecurityException("Selected service does not require "
503 + android.Manifest.permission.BIND_REMOTEVIEWS
504 + ": " + componentName);
505 }
506 } catch (PackageManager.NameNotFoundException e) {
507 throw new IllegalArgumentException("Unknown component " + componentName);
508 }
509
Winson Chung16c8d8a2011-01-20 16:19:33 -0800510 // If there is already a connection made for this service intent, then disconnect from
511 // that first. (This does not allow multiple connections to the same service under
512 // the same key)
513 ServiceConnectionProxy conn = null;
Winson Chung84bbb022011-02-21 13:57:45 -0800514 FilterComparison fc = new FilterComparison(intent);
515 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800516 if (mBoundRemoteViewsServices.containsKey(key)) {
517 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
518 conn.disconnect();
519 mContext.unbindService(conn);
520 mBoundRemoteViewsServices.remove(key);
521 }
522
523 // Bind to the RemoteViewsService (which will trigger a callback to the
524 // RemoteViewsAdapter.onServiceConnected())
Winson Chung81f39eb2011-01-11 18:05:01 -0800525 final long token = Binder.clearCallingIdentity();
526 try {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800527 conn = new ServiceConnectionProxy(key, connection);
Winson Chung81f39eb2011-01-11 18:05:01 -0800528 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
529 mBoundRemoteViewsServices.put(key, conn);
530 } finally {
531 Binder.restoreCallingIdentity(token);
532 }
Winson Chung84bbb022011-02-21 13:57:45 -0800533
534 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
535 // when we can call back to the RemoteViewsService later to destroy associated
536 // factories.
Winson Chung22bc69d2011-02-25 15:13:38 -0800537 incrementAppWidgetServiceRefCount(appWidgetId, fc);
Winson Chung81f39eb2011-01-11 18:05:01 -0800538 }
539 }
540
Winson Chung84bbb022011-02-21 13:57:45 -0800541 // Unbinds from a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800542 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
543 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700544 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800545 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
546 // RemoteViewsAdapter)
547 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
548 new FilterComparison(intent));
549 if (mBoundRemoteViewsServices.containsKey(key)) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800550 // We don't need to use the appWidgetId until after we are sure there is something
551 // to unbind. Note that this may mask certain issues with apps calling unbind()
552 // more than necessary.
553 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
554 if (id == null) {
555 throw new IllegalArgumentException("bad appWidgetId");
556 }
557
558 ServiceConnectionProxy conn =
559 (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
560 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800561 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800562 mBoundRemoteViewsServices.remove(key);
563 } else {
564 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
Winson Chung81f39eb2011-01-11 18:05:01 -0800565 }
566 }
567 }
568
Winson Chung84bbb022011-02-21 13:57:45 -0800569 // Unbinds from a RemoteViewsService when we delete an app widget
Winson Chung81f39eb2011-01-11 18:05:01 -0800570 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800571 int appWidgetId = id.appWidgetId;
572 // Unbind all connections to Services bound to this AppWidgetId
Winson Chung81f39eb2011-01-11 18:05:01 -0800573 Iterator<Pair<Integer, Intent.FilterComparison>> it =
574 mBoundRemoteViewsServices.keySet().iterator();
Winson Chung81f39eb2011-01-11 18:05:01 -0800575 while (it.hasNext()) {
576 final Pair<Integer, Intent.FilterComparison> key = it.next();
577 if (key.first.intValue() == appWidgetId) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800578 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
579 mBoundRemoteViewsServices.get(key);
580 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800581 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800582 it.remove();
Winson Chung81f39eb2011-01-11 18:05:01 -0800583 }
584 }
Winson Chung84bbb022011-02-21 13:57:45 -0800585
586 // Check if we need to destroy any services (if no other app widgets are
587 // referencing the same service)
588 decrementAppWidgetServiceRefCount(appWidgetId);
589 }
590
591 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
592 private void destroyRemoteViewsService(final Intent intent) {
593 final ServiceConnection conn = new ServiceConnection() {
594 @Override
595 public void onServiceConnected(ComponentName name, IBinder service) {
596 final IRemoteViewsFactory cb =
597 IRemoteViewsFactory.Stub.asInterface(service);
598 try {
599 cb.onDestroy(intent);
Adam Cohen2625fea2011-03-23 17:24:30 -0700600 } catch (RemoteException e) {
601 e.printStackTrace();
602 } catch (RuntimeException e) {
Winson Chung84bbb022011-02-21 13:57:45 -0800603 e.printStackTrace();
604 }
605 mContext.unbindService(this);
606 }
607 @Override
608 public void onServiceDisconnected(android.content.ComponentName name) {
609 // Do nothing
610 }
611 };
612
613 // Bind to the service and remove the static intent->factory mapping in the
614 // RemoteViewsService.
615 final long token = Binder.clearCallingIdentity();
616 try {
617 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
618 } finally {
619 Binder.restoreCallingIdentity(token);
620 }
621 }
622
623 // Adds to the ref-count for a given RemoteViewsService intent
624 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
625 HashSet<Integer> appWidgetIds = null;
626 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
627 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
628 } else {
629 appWidgetIds = new HashSet<Integer>();
630 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
631 }
632 appWidgetIds.add(appWidgetId);
633 }
634
635 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
636 // the ref-count reaches zero.
637 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
638 Iterator<FilterComparison> it =
639 mRemoteViewsServicesAppWidgets.keySet().iterator();
640 while (it.hasNext()) {
641 final FilterComparison key = it.next();
642 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
643 if (ids.remove(appWidgetId)) {
644 // If we have removed the last app widget referencing this service, then we
645 // should destroy it and remove it from this set
646 if (ids.isEmpty()) {
647 destroyRemoteViewsService(key.getIntent());
648 it.remove();
649 }
650 }
651 }
Winson Chung81f39eb2011-01-11 18:05:01 -0800652 }
653
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700654 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
655 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700656 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700657 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 if (id != null && id.provider != null && !id.provider.zombie) {
659 return id.provider.info;
660 }
661 return null;
662 }
663 }
664
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700665 public RemoteViews getAppWidgetViews(int appWidgetId) {
666 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700667 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700668 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 if (id != null) {
670 return id.views;
671 }
672 return null;
673 }
674 }
675
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700676 public List<AppWidgetProviderInfo> getInstalledProviders() {
677 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700678 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700680 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 for (int i=0; i<N; i++) {
682 Provider p = mInstalledProviders.get(i);
683 if (!p.zombie) {
684 result.add(p.info);
685 }
686 }
687 return result;
688 }
689 }
690
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700691 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
692 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 return;
694 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700695 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 return;
697 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700698 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700700 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700701 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700703 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
704 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 }
706 }
707 }
708
Adam Cohen2dd21972010-08-15 18:20:04 -0700709 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
710 if (appWidgetIds == null) {
711 return;
712 }
713 if (appWidgetIds.length == 0) {
714 return;
715 }
716 final int N = appWidgetIds.length;
717
718 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700719 ensureStateLoadedLocked();
Adam Cohen2dd21972010-08-15 18:20:04 -0700720 for (int i=0; i<N; i++) {
721 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
722 updateAppWidgetInstanceLocked(id, views, true);
723 }
724 }
725 }
726
Winson Chung6394c0e2010-08-16 10:14:56 -0700727 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700728 if (appWidgetIds == null) {
729 return;
730 }
731 if (appWidgetIds.length == 0) {
732 return;
733 }
734 final int N = appWidgetIds.length;
735
736 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700737 ensureStateLoadedLocked();
Winson Chung499cb9f2010-07-16 11:18:17 -0700738 for (int i=0; i<N; i++) {
739 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700740 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700741 }
742 }
743 }
744
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700745 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
746 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700747 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800748 Provider p = lookupProviderLocked(provider);
749 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800750 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 return;
752 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700753 ArrayList<AppWidgetId> instances = p.instances;
Jeff Sharkey933deca2011-11-03 11:26:11 -0700754 final int callingUid = getCallingUid();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 final int N = instances.size();
756 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700757 AppWidgetId id = instances.get(i);
Jeff Sharkey933deca2011-11-03 11:26:11 -0700758 if (canAccessAppWidgetId(id, callingUid)) {
759 updateAppWidgetInstanceLocked(id, views);
760 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 }
762 }
763 }
764
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700765 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700766 updateAppWidgetInstanceLocked(id, views, false);
767 }
768
769 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700770 // allow for stale appWidgetIds and other badness
771 // lookup also checks that the calling process can access the appWidgetId
772 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700774
775 // We do not want to save this RemoteViews
776 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777
778 // is anyone listening?
779 if (id.host.callbacks != null) {
780 try {
781 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700782 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 } catch (RemoteException e) {
784 // It failed; remove the callback. No need to prune because
785 // we know that this host is still referenced by this instance.
786 id.host.callbacks = null;
787 }
788 }
789 }
790 }
791
Winson Chung6394c0e2010-08-16 10:14:56 -0700792 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700793 // allow for stale appWidgetIds and other badness
794 // lookup also checks that the calling process can access the appWidgetId
795 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
796 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700797 // is anyone listening?
798 if (id.host.callbacks != null) {
799 try {
800 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700801 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700802 } catch (RemoteException e) {
803 // It failed; remove the callback. No need to prune because
804 // we know that this host is still referenced by this instance.
805 id.host.callbacks = null;
806 }
807 }
808 }
809 }
810
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700811 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 List<RemoteViews> updatedViews) {
813 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700814 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700815 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
817 host.callbacks = callbacks;
818
819 updatedViews.clear();
820
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700821 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 int N = instances.size();
823 int[] updatedIds = new int[N];
824 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700825 AppWidgetId id = instances.get(i);
826 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 updatedViews.add(id.views);
828 }
829 return updatedIds;
830 }
831 }
832
833 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700834 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700835 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700837 if (host != null) {
838 host.callbacks = null;
839 pruneHostLocked(host);
840 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 }
842 }
843
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700844 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700846 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 return true;
848 }
849 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700850 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 return true;
852 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700853 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700855 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 return true;
857 }
858 // Nobody else can access it.
859 return false;
860 }
861
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700862 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700864 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700866 AppWidgetId id = mAppWidgetIds.get(i);
867 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 return id;
869 }
870 }
871 return null;
872 }
873
874 Provider lookupProviderLocked(ComponentName provider) {
875 final int N = mInstalledProviders.size();
876 for (int i=0; i<N; i++) {
877 Provider p = mInstalledProviders.get(i);
Adam Cohenbac26a122011-08-16 20:42:30 -0700878 if (p.info.provider.equals(provider)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 return p;
880 }
881 }
882 return null;
883 }
884
885 Host lookupHostLocked(int uid, int hostId) {
886 final int N = mHosts.size();
887 for (int i=0; i<N; i++) {
888 Host h = mHosts.get(i);
889 if (h.uid == uid && h.hostId == hostId) {
890 return h;
891 }
892 }
893 return null;
894 }
895
896 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
897 final int N = mHosts.size();
898 for (int i=0; i<N; i++) {
899 Host h = mHosts.get(i);
900 if (h.hostId == hostId && h.packageName.equals(packageName)) {
901 return h;
902 }
903 }
904 Host host = new Host();
905 host.packageName = packageName;
906 host.uid = uid;
907 host.hostId = hostId;
908 mHosts.add(host);
909 return host;
910 }
911
912 void pruneHostLocked(Host host) {
913 if (host.instances.size() == 0 && host.callbacks == null) {
914 mHosts.remove(host);
915 }
916 }
917
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700918 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 PackageManager pm = mPackageManager;
920
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700921 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
923 PackageManager.GET_META_DATA);
924
Bjorn Bringert5f857802010-02-10 23:09:48 +0000925 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 for (int i=0; i<N; i++) {
927 ResolveInfo ri = broadcastReceivers.get(i);
928 addProviderLocked(ri);
929 }
930 }
931
932 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800933 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
934 return false;
935 }
936 if (!ri.activityInfo.isEnabled()) {
937 return false;
938 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
940 ri.activityInfo.name), ri);
941 if (p != null) {
942 mInstalledProviders.add(p);
943 return true;
944 } else {
945 return false;
946 }
947 }
948
949 void removeProviderLocked(int index, Provider p) {
950 int N = p.instances.size();
951 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700952 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800953 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700954 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 // Stop telling the host about updates for this from now on
956 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700957 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700959 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 id.provider = null;
961 pruneHostLocked(id.host);
962 id.host = null;
963 }
964 p.instances.clear();
965 mInstalledProviders.remove(index);
Adam Cohen7bb98832011-10-05 18:10:13 -0700966 mDeletedProviders.add(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 // no need to send the DISABLE broadcast, since the receiver is gone anyway
968 cancelBroadcasts(p);
969 }
970
971 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700972 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 intent.setComponent(p.info.provider);
974 mContext.sendBroadcast(intent);
975 }
976
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700977 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
978 if (appWidgetIds != null && appWidgetIds.length > 0) {
979 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
980 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 intent.setComponent(p.info.provider);
982 mContext.sendBroadcast(intent);
983 }
984 }
985
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700986 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 if (p.info.updatePeriodMillis > 0) {
988 // if this is the first instance, set the alarm. otherwise,
989 // rely on the fact that we've already set it and that
990 // PendingIntent.getBroadcast will update the extras.
991 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700992 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
993 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 intent.setComponent(p.info.provider);
995 long token = Binder.clearCallingIdentity();
996 try {
997 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
998 PendingIntent.FLAG_UPDATE_CURRENT);
999 } finally {
1000 Binder.restoreCallingIdentity(token);
1001 }
1002 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -07001003 long period = p.info.updatePeriodMillis;
1004 if (period < MIN_UPDATE_PERIOD) {
1005 period = MIN_UPDATE_PERIOD;
1006 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -07001008 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 }
1010 }
1011 }
1012
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001013 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001015 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001017 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001019 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 }
1021
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001022 public int[] getAppWidgetIds(ComponentName provider) {
1023 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001024 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 Provider p = lookupProviderLocked(provider);
1026 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001027 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 } else {
1029 return new int[0];
1030 }
1031 }
1032 }
1033
1034 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1035 Provider p = null;
1036
1037 ActivityInfo activityInfo = ri.activityInfo;
1038 XmlResourceParser parser = null;
1039 try {
1040 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001041 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001043 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001044 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 return null;
1046 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001047
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -08001049
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 int type;
1051 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1052 && type != XmlPullParser.START_TAG) {
1053 // drain whitespace, comments, etc.
1054 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001057 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001058 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001059 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 return null;
1061 }
1062
1063 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001064 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 info.provider = component;
1066 p.uid = activityInfo.applicationInfo.uid;
1067
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001068 Resources res = mPackageManager.getResourcesForApplication(
1069 activityInfo.applicationInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001070
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001071 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001072 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001073
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001074 // These dimensions has to be resolved in the application's context.
1075 // We simply send back the raw complex data, which will be
1076 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1077 TypedValue value = sa.peekValue(
1078 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1079 info.minWidth = value != null ? value.data : 0;
1080 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1081 info.minHeight = value != null ? value.data : 0;
Adam Cohen1bfaf562011-07-19 18:05:33 -07001082 value = sa.peekValue(
1083 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1084 info.minResizeWidth = value != null ? value.data : info.minWidth;
1085 value = sa.peekValue(
1086 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1087 info.minResizeHeight = value != null ? value.data : info.minHeight;
Adam Cohend2e20de2011-02-25 12:03:37 -08001088
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001090 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001092 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001094 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095 if (className != null) {
1096 info.configure = new ComponentName(component.getPackageName(), className);
1097 }
1098 info.label = activityInfo.loadLabel(mPackageManager).toString();
1099 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001100 info.previewImage = sa.getResourceId(
1101 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -07001102 info.autoAdvanceViewId = sa.getResourceId(
1103 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Adam Cohen9611f2e2011-02-28 13:39:38 -08001104 info.resizeMode = sa.getInt(
Adam Cohend2e20de2011-02-25 12:03:37 -08001105 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1106 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 sa.recycle();
1109 } catch (Exception e) {
1110 // Ok to catch Exception here, because anything going wrong because
1111 // of what a client process passes to us should not be fatal for the
1112 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001113 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 return null;
1115 } finally {
1116 if (parser != null) parser.close();
1117 }
1118 return p;
1119 }
1120
1121 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1122 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1123 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1124 throw new PackageManager.NameNotFoundException();
1125 }
1126 return pkgInfo.applicationInfo.uid;
1127 }
1128
1129 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1130 int callingUid = getCallingUid();
1131 int packageUid;
1132 try {
1133 packageUid = getUidForPackage(packageName);
1134 } catch (PackageManager.NameNotFoundException ex) {
1135 throw new IllegalArgumentException("packageName and uid don't match packageName="
1136 + packageName);
1137 }
Jeff Brown10e89712011-07-08 18:52:57 -07001138 if (callingUid != packageUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 throw new IllegalArgumentException("packageName and uid don't match packageName="
1140 + packageName);
1141 }
1142 return callingUid;
1143 }
1144
1145 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001146 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001147 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 final int N = mInstalledProviders.size();
1149 for (int i=0; i<N; i++) {
1150 Provider p = mInstalledProviders.get(i);
1151 if (p.instances.size() > 0) {
1152 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001153 int[] appWidgetIds = getAppWidgetIds(p);
1154 sendUpdateIntentLocked(p, appWidgetIds);
1155 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001156 }
1157 }
1158 }
1159 }
1160
1161 // only call from initialization -- it assumes that the data structures are all empty
1162 void loadStateLocked() {
Adam Cohen97300312011-10-12 15:48:13 -07001163 AtomicFile file = savedStateFile();
1164 try {
1165 FileInputStream stream = file.openRead();
1166 readStateFromFileLocked(stream);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167
Adam Cohen97300312011-10-12 15:48:13 -07001168 if (stream != null) {
1169 try {
1170 stream.close();
1171 } catch (IOException e) {
1172 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1173 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 }
Adam Cohen97300312011-10-12 15:48:13 -07001175 } catch (FileNotFoundException e) {
1176 Slog.w(TAG, "Failed to read state: " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 }
1178 }
Adam Cohen97300312011-10-12 15:48:13 -07001179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 void saveStateLocked() {
Adam Cohen97300312011-10-12 15:48:13 -07001181 AtomicFile file = savedStateFile();
1182 FileOutputStream stream;
1183 try {
1184 stream = file.startWrite();
1185 if (writeStateToFileLocked(stream)) {
1186 file.finishWrite(stream);
1187 } else {
1188 file.failWrite(stream);
1189 Slog.w(TAG, "Failed to save state, restoring backup.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 }
Adam Cohen97300312011-10-12 15:48:13 -07001191 } catch (IOException e) {
1192 Slog.w(TAG, "Failed open state file for write: " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 }
1195
Adam Cohen97300312011-10-12 15:48:13 -07001196 boolean writeStateToFileLocked(FileOutputStream stream) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 int N;
1198
1199 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 XmlSerializer out = new FastXmlSerializer();
1201 out.setOutput(stream, "utf-8");
1202 out.startDocument(null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001203 out.startTag(null, "gs");
1204
1205 int providerIndex = 0;
1206 N = mInstalledProviders.size();
1207 for (int i=0; i<N; i++) {
1208 Provider p = mInstalledProviders.get(i);
1209 if (p.instances.size() > 0) {
1210 out.startTag(null, "p");
1211 out.attribute(null, "pkg", p.info.provider.getPackageName());
1212 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001213 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 p.tag = providerIndex;
1215 providerIndex++;
1216 }
1217 }
1218
1219 N = mHosts.size();
1220 for (int i=0; i<N; i++) {
1221 Host host = mHosts.get(i);
1222 out.startTag(null, "h");
1223 out.attribute(null, "pkg", host.packageName);
1224 out.attribute(null, "id", Integer.toHexString(host.hostId));
1225 out.endTag(null, "h");
1226 host.tag = i;
1227 }
1228
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001229 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001231 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001233 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001234 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1235 if (id.provider != null) {
1236 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1237 }
1238 out.endTag(null, "g");
1239 }
1240
1241 out.endTag(null, "gs");
1242
1243 out.endDocument();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001244 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 } catch (IOException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001246 Slog.w(TAG, "Failed to write state: " + e);
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001247 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 }
1249 }
1250
Adam Cohen97300312011-10-12 15:48:13 -07001251 void readStateFromFileLocked(FileInputStream stream) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252 boolean success = false;
1253
1254 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 XmlPullParser parser = Xml.newPullParser();
1256 parser.setInput(stream, null);
1257
1258 int type;
1259 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001260 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 do {
1262 type = parser.next();
1263 if (type == XmlPullParser.START_TAG) {
1264 String tag = parser.getName();
1265 if ("p".equals(tag)) {
1266 // TODO: do we need to check that this package has the same signature
1267 // as before?
1268 String pkg = parser.getAttributeValue(null, "pkg");
1269 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001270
1271 final PackageManager packageManager = mContext.getPackageManager();
1272 try {
1273 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1274 } catch (PackageManager.NameNotFoundException e) {
1275 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1276 new String[] { pkg });
1277 pkg = pkgs[0];
1278 }
1279
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1281 if (p == null && mSafeMode) {
1282 // if we're in safe mode, make a temporary one
1283 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001284 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 p.info.provider = new ComponentName(pkg, cl);
1286 p.zombie = true;
1287 mInstalledProviders.add(p);
1288 }
1289 if (p != null) {
1290 // if it wasn't uninstalled or something
1291 loadedProviders.put(providerIndex, p);
1292 }
1293 providerIndex++;
1294 }
1295 else if ("h".equals(tag)) {
1296 Host host = new Host();
1297
1298 // TODO: do we need to check that this package has the same signature
1299 // as before?
1300 host.packageName = parser.getAttributeValue(null, "pkg");
1301 try {
1302 host.uid = getUidForPackage(host.packageName);
1303 } catch (PackageManager.NameNotFoundException ex) {
1304 host.zombie = true;
1305 }
1306 if (!host.zombie || mSafeMode) {
1307 // In safe mode, we don't discard the hosts we don't recognize
1308 // so that they're not pruned from our list. Otherwise, we do.
1309 host.hostId = Integer.parseInt(
1310 parser.getAttributeValue(null, "id"), 16);
1311 mHosts.add(host);
1312 }
1313 }
1314 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001315 AppWidgetId id = new AppWidgetId();
1316 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1317 if (id.appWidgetId >= mNextAppWidgetId) {
1318 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001319 }
1320
1321 String providerString = parser.getAttributeValue(null, "p");
1322 if (providerString != null) {
1323 // there's no provider if it hasn't been bound yet.
1324 // maybe we don't have to save this, but it brings the system
1325 // to the state it was in.
1326 int pIndex = Integer.parseInt(providerString, 16);
1327 id.provider = loadedProviders.get(pIndex);
1328 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001329 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 + pIndex + " which is " + id.provider);
1331 }
1332 if (id.provider == null) {
1333 // This provider is gone. We just let the host figure out
1334 // that this happened when it fails to load it.
1335 continue;
1336 }
1337 }
1338
1339 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1340 id.host = mHosts.get(hIndex);
1341 if (id.host == null) {
1342 // This host is gone.
1343 continue;
1344 }
1345
1346 if (id.provider != null) {
1347 id.provider.instances.add(id);
1348 }
1349 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001350 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 }
1352 }
1353 } while (type != XmlPullParser.END_DOCUMENT);
1354 success = true;
1355 } catch (NullPointerException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001356 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 } catch (NumberFormatException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001358 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 } catch (XmlPullParserException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001360 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 } catch (IOException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001362 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 } catch (IndexOutOfBoundsException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001364 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 }
1366
1367 if (success) {
1368 // delete any hosts that didn't manage to get connected (should happen)
1369 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001370 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 pruneHostLocked(mHosts.get(i));
1372 }
1373 } else {
1374 // failed reading, clean up
Adam Cohen97300312011-10-12 15:48:13 -07001375 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1376
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001377 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001378 mHosts.clear();
1379 final int N = mInstalledProviders.size();
1380 for (int i=0; i<N; i++) {
1381 mInstalledProviders.get(i).instances.clear();
1382 }
1383 }
1384 }
1385
Adam Cohen97300312011-10-12 15:48:13 -07001386 AtomicFile savedStateFile() {
1387 return new AtomicFile(new File("/data/system/" + SETTINGS_FILENAME));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388 }
1389
1390 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1391 public void onReceive(Context context, Intent intent) {
1392 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001393 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001394 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1395 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001396 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1397 Locale revised = Locale.getDefault();
1398 if (revised == null || mLocale == null ||
1399 !(revised.equals(mLocale))) {
1400 mLocale = revised;
1401
1402 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001403 ensureStateLoadedLocked();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001404 int N = mInstalledProviders.size();
1405 for (int i=N-1; i>=0; i--) {
1406 Provider p = mInstalledProviders.get(i);
1407 String pkgName = p.info.provider.getPackageName();
1408 updateProvidersForPackageLocked(pkgName);
1409 }
1410 saveStateLocked();
1411 }
1412 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001414 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001415 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001416 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001417 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001418 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1419 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001420 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001421 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1422 added = false;
1423 } else {
1424 Uri uri = intent.getData();
1425 if (uri == null) {
1426 return;
1427 }
1428 String pkgName = uri.getSchemeSpecificPart();
1429 if (pkgName == null) {
1430 return;
1431 }
1432 pkgList = new String[] { pkgName };
1433 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001434 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001435 }
1436 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001437 return;
1438 }
Joe Onoratod070e892011-01-07 20:50:37 -08001439 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001440 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001441 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001443 if (changed || (extras != null &&
1444 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001445 for (String pkgName : pkgList) {
1446 // The package was just upgraded
1447 updateProvidersForPackageLocked(pkgName);
1448 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 } else {
1450 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001451 for (String pkgName : pkgList) {
1452 addProvidersForPackageLocked(pkgName);
1453 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 }
1455 saveStateLocked();
1456 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001457 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001458 Bundle extras = intent.getExtras();
1459 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1460 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1461 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001462 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001463 ensureStateLoadedLocked();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001464 for (String pkgName : pkgList) {
1465 removeProvidersForPackageLocked(pkgName);
1466 saveStateLocked();
1467 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 }
1469 }
1470 }
1471 }
1472 }
1473 };
1474
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001476 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001477 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1479 PackageManager.GET_META_DATA);
1480
Bjorn Bringert5f857802010-02-10 23:09:48 +00001481 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 for (int i=0; i<N; i++) {
1483 ResolveInfo ri = broadcastReceivers.get(i);
1484 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001485 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1486 continue;
1487 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 if (pkgName.equals(ai.packageName)) {
1489 addProviderLocked(ri);
1490 }
1491 }
1492 }
1493
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001494 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001495 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001496 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001497 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001498 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1499 PackageManager.GET_META_DATA);
1500
1501 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001502 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 for (int i=0; i<N; i++) {
1504 ResolveInfo ri = broadcastReceivers.get(i);
1505 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001506 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1507 continue;
1508 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001509 if (pkgName.equals(ai.packageName)) {
1510 ComponentName component = new ComponentName(ai.packageName, ai.name);
1511 Provider p = lookupProviderLocked(component);
1512 if (p == null) {
1513 if (addProviderLocked(ri)) {
1514 keep.add(ai.name);
1515 }
1516 } else {
1517 Provider parsed = parseProviderInfoXml(component, ri);
1518 if (parsed != null) {
1519 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001520 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001521 p.info = parsed.info;
1522 // If it's enabled
1523 final int M = p.instances.size();
1524 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001525 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 // Reschedule for the new updatePeriodMillis (don't worry about handling
1527 // it specially if updatePeriodMillis didn't change because we just sent
1528 // an update, and the next one will be updatePeriodMillis from now).
1529 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001530 registerForBroadcastsLocked(p, appWidgetIds);
1531 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001533 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001534 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 if (id.host != null && id.host.callbacks != null) {
1536 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001537 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001538 } catch (RemoteException ex) {
1539 // It failed; remove the callback. No need to prune because
1540 // we know that this host is still referenced by this
1541 // instance.
1542 id.host.callbacks = null;
1543 }
1544 }
1545 }
1546 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001547 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001548 }
1549 }
1550 }
1551 }
1552 }
1553
1554 // prune the ones we don't want to keep
1555 N = mInstalledProviders.size();
1556 for (int i=N-1; i>=0; i--) {
1557 Provider p = mInstalledProviders.get(i);
1558 if (pkgName.equals(p.info.provider.getPackageName())
1559 && !keep.contains(p.info.provider.getClassName())) {
1560 removeProviderLocked(i, p);
1561 }
1562 }
1563 }
1564
1565 void removeProvidersForPackageLocked(String pkgName) {
1566 int N = mInstalledProviders.size();
1567 for (int i=N-1; i>=0; i--) {
1568 Provider p = mInstalledProviders.get(i);
1569 if (pkgName.equals(p.info.provider.getPackageName())) {
1570 removeProviderLocked(i, p);
1571 }
1572 }
1573
1574 // Delete the hosts for this package too
1575 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001576 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001577 // so we don't need to worry about sending DISABLE broadcasts to them.
1578 N = mHosts.size();
1579 for (int i=N-1; i>=0; i--) {
1580 Host host = mHosts.get(i);
1581 if (pkgName.equals(host.packageName)) {
1582 deleteHostLocked(host);
1583 }
1584 }
1585 }
1586}
1587