blob: 731fb22e268573e0d2aa858c39afe600f68a58f4 [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;
28import android.content.pm.ActivityInfo;
Joe Onorato331fbdc2010-08-24 17:02:09 -040029import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.pm.PackageManager;
31import android.content.pm.PackageInfo;
32import android.content.pm.ResolveInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080033import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.res.TypedArray;
35import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.net.Uri;
37import android.os.Binder;
38import android.os.Bundle;
Marco Nelissen54796e72009-04-30 15:16:30 -070039import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.os.RemoteException;
41import android.os.SystemClock;
42import android.util.AttributeSet;
Joe Onorato8a9b2202010-02-26 18:56:32 -080043import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070044import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.util.Xml;
46import android.widget.RemoteViews;
47
48import java.io.IOException;
49import java.io.File;
50import java.io.FileDescriptor;
51import java.io.FileInputStream;
52import java.io.FileOutputStream;
53import java.io.PrintWriter;
54import java.util.ArrayList;
55import java.util.List;
Eric Fischer63c2d9e2009-10-22 15:22:50 -070056import java.util.Locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import java.util.HashMap;
58import java.util.HashSet;
59
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070060import com.android.internal.appwidget.IAppWidgetService;
61import com.android.internal.appwidget.IAppWidgetHost;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080062import com.android.internal.util.FastXmlSerializer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
64import org.xmlpull.v1.XmlPullParser;
65import org.xmlpull.v1.XmlPullParserException;
66import org.xmlpull.v1.XmlSerializer;
67
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070068class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070070 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070072 private static final String SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070074 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075
76 /*
77 * When identifying a Host or Provider based on the calling process, use the uid field.
78 * When identifying a Host or Provider based on a package manager broadcast, use the
79 * package given.
80 */
81
82 static class Provider {
83 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070084 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070085 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 PendingIntent broadcast;
87 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
88
89 int tag; // for use while saving state (the index)
90 }
91
92 static class Host {
93 int uid;
94 int hostId;
95 String packageName;
Romain Guya5475592009-07-01 17:20:08 -070096 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070097 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
99
100 int tag; // for use while saving state (the index)
101 }
102
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700103 static class AppWidgetId {
104 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 Provider provider;
106 RemoteViews views;
107 Host host;
108 }
109
110 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700111 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 PackageManager mPackageManager;
113 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700114 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700115 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700116 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
117 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 boolean mSafeMode;
119
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700120 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 mContext = context;
122 mPackageManager = context.getPackageManager();
123 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
124 }
125
126 public void systemReady(boolean safeMode) {
127 mSafeMode = safeMode;
128
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700129 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 loadStateLocked();
131
132 // Register for the boot completed broadcast, so we can send the
133 // ENABLE broacasts. If we try to send them now, they time out,
134 // because the system isn't ready to handle them yet.
135 mContext.registerReceiver(mBroadcastReceiver,
136 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
137
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700138 // Register for configuration changes so we can update the names
139 // of the widgets when the locale changes.
140 mContext.registerReceiver(mBroadcastReceiver,
141 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 // Register for broadcasts about package install, etc., so we can
144 // update the provider list.
145 IntentFilter filter = new IntentFilter();
146 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
147 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
148 filter.addDataScheme("package");
149 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800150 // Register for events related to sdcard installation.
151 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800152 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
153 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800154 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 }
156
157 @Override
158 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
159 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
160 != PackageManager.PERMISSION_GRANTED) {
161 pw.println("Permission Denial: can't dump from from pid="
162 + Binder.getCallingPid()
163 + ", uid=" + Binder.getCallingUid());
164 return;
165 }
166
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700167 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700169 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 for (int i=0; i<N; i++) {
171 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700172 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700173 pw.print(" ["); pw.print(i); pw.print("] provider ");
174 pw.print(info.provider.flattenToShortString());
175 pw.println(':');
176 pw.print(" min=("); pw.print(info.minWidth);
177 pw.print("x"); pw.print(info.minHeight);
178 pw.print(") updatePeriodMillis=");
179 pw.print(info.updatePeriodMillis);
180 pw.print(" initialLayout=#");
181 pw.print(Integer.toHexString(info.initialLayout));
182 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 }
184
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700185 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700186 pw.println(" ");
187 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700189 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700190 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700191 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700192 pw.print(" hostId=");
193 pw.print(id.host.hostId); pw.print(' ');
194 pw.print(id.host.packageName); pw.print('/');
195 pw.println(id.host.uid);
196 if (id.provider != null) {
197 pw.print(" provider=");
198 pw.println(id.provider.info.provider.flattenToShortString());
199 }
200 if (id.host != null) {
201 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
202 }
203 if (id.views != null) {
204 pw.print(" views="); pw.println(id.views);
205 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
207
208 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700209 pw.println(" ");
210 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 for (int i=0; i<N; i++) {
212 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700213 pw.print(" ["); pw.print(i); pw.print("] hostId=");
214 pw.print(host.hostId); pw.print(' ');
215 pw.print(host.packageName); pw.print('/');
216 pw.print(host.uid); pw.println(':');
217 pw.print(" callbacks="); pw.println(host.callbacks);
218 pw.print(" instances.size="); pw.print(host.instances.size());
219 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 }
221 }
222 }
223
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700224 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700226 synchronized (mAppWidgetIds) {
227 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228
229 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
230
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700231 AppWidgetId id = new AppWidgetId();
232 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 id.host = host;
234
235 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700236 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237
238 saveStateLocked();
239
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700240 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 }
242 }
243
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700244 public void deleteAppWidgetId(int appWidgetId) {
245 synchronized (mAppWidgetIds) {
246 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700248 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 saveStateLocked();
250 }
251 }
252 }
253
254 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700255 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 int callingUid = getCallingUid();
257 Host host = lookupHostLocked(callingUid, hostId);
258 if (host != null) {
259 deleteHostLocked(host);
260 saveStateLocked();
261 }
262 }
263 }
264
265 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700266 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 int callingUid = getCallingUid();
268 final int N = mHosts.size();
269 boolean changed = false;
270 for (int i=N-1; i>=0; i--) {
271 Host host = mHosts.get(i);
272 if (host.uid == callingUid) {
273 deleteHostLocked(host);
274 changed = true;
275 }
276 }
277 if (changed) {
278 saveStateLocked();
279 }
280 }
281 }
282
283 void deleteHostLocked(Host host) {
284 final int N = host.instances.size();
285 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700286 AppWidgetId id = host.instances.get(i);
287 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 }
289 host.instances.clear();
290 mHosts.remove(host);
291 // it's gone or going away, abruptly drop the callback connection
292 host.callbacks = null;
293 }
294
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700295 void deleteAppWidgetLocked(AppWidgetId id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 Host host = id.host;
297 host.instances.remove(id);
298 pruneHostLocked(host);
299
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700300 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301
302 Provider p = id.provider;
303 if (p != null) {
304 p.instances.remove(id);
305 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700306 // send the broacast saying that this appWidgetId has been deleted
307 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700309 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 mContext.sendBroadcast(intent);
311 if (p.instances.size() == 0) {
312 // cancel the future updates
313 cancelBroadcasts(p);
314
315 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700316 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 intent.setComponent(p.info.provider);
318 mContext.sendBroadcast(intent);
319 }
320 }
321 }
322 }
323
324 void cancelBroadcasts(Provider p) {
325 if (p.broadcast != null) {
326 mAlarmManager.cancel(p.broadcast);
327 long token = Binder.clearCallingIdentity();
328 try {
329 p.broadcast.cancel();
330 } finally {
331 Binder.restoreCallingIdentity(token);
332 }
333 p.broadcast = null;
334 }
335 }
336
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700337 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
338 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
339 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
340 synchronized (mAppWidgetIds) {
341 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700343 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 }
345 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700346 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 + id.provider.info.provider);
348 }
349 Provider p = lookupProviderLocked(provider);
350 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700351 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 }
353 if (p.zombie) {
354 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
355 + " safe mode: " + provider);
356 }
357
358 id.provider = p;
359 p.instances.add(id);
360 int instancesSize = p.instances.size();
361 if (instancesSize == 1) {
362 // tell the provider that it's ready
363 sendEnableIntentLocked(p);
364 }
365
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700366 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 // It's less critical when the next one happens, so when we schdule the next one,
368 // we add updatePeriodMillis to its start time. That time will have some slop,
369 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700370 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371
372 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700373 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 saveStateLocked();
375 }
376 }
377
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700378 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
379 synchronized (mAppWidgetIds) {
380 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 if (id != null && id.provider != null && !id.provider.zombie) {
382 return id.provider.info;
383 }
384 return null;
385 }
386 }
387
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700388 public RemoteViews getAppWidgetViews(int appWidgetId) {
389 synchronized (mAppWidgetIds) {
390 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 if (id != null) {
392 return id.views;
393 }
394 return null;
395 }
396 }
397
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700398 public List<AppWidgetProviderInfo> getInstalledProviders() {
399 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700401 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 for (int i=0; i<N; i++) {
403 Provider p = mInstalledProviders.get(i);
404 if (!p.zombie) {
405 result.add(p.info);
406 }
407 }
408 return result;
409 }
410 }
411
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700412 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
413 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 return;
415 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700416 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 return;
418 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700419 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700421 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700423 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
424 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 }
426 }
427 }
428
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700429 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
430 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 Provider p = lookupProviderLocked(provider);
432 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800433 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 return;
435 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700436 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 final int N = instances.size();
438 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700439 AppWidgetId id = instances.get(i);
440 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 }
442 }
443 }
444
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700445 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
446 // allow for stale appWidgetIds and other badness
447 // lookup also checks that the calling process can access the appWidgetId
448 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
450 id.views = views;
451
452 // is anyone listening?
453 if (id.host.callbacks != null) {
454 try {
455 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700456 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 } catch (RemoteException e) {
458 // It failed; remove the callback. No need to prune because
459 // we know that this host is still referenced by this instance.
460 id.host.callbacks = null;
461 }
462 }
463 }
464 }
465
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700466 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 List<RemoteViews> updatedViews) {
468 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700469 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
471 host.callbacks = callbacks;
472
473 updatedViews.clear();
474
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700475 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 int N = instances.size();
477 int[] updatedIds = new int[N];
478 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700479 AppWidgetId id = instances.get(i);
480 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 updatedViews.add(id.views);
482 }
483 return updatedIds;
484 }
485 }
486
487 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700488 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700490 if (host != null) {
491 host.callbacks = null;
492 pruneHostLocked(host);
493 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 }
495 }
496
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700497 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700499 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 return true;
501 }
502 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700503 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 return true;
505 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700506 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700508 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 return true;
510 }
511 // Nobody else can access it.
512 return false;
513 }
514
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700515 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700517 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700519 AppWidgetId id = mAppWidgetIds.get(i);
520 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 return id;
522 }
523 }
524 return null;
525 }
526
527 Provider lookupProviderLocked(ComponentName provider) {
Romain Guyd2671e12010-03-11 18:06:42 -0800528 final String className = provider.getClassName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 final int N = mInstalledProviders.size();
530 for (int i=0; i<N; i++) {
531 Provider p = mInstalledProviders.get(i);
Romain Guyd2671e12010-03-11 18:06:42 -0800532 if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 return p;
534 }
535 }
536 return null;
537 }
538
539 Host lookupHostLocked(int uid, int hostId) {
540 final int N = mHosts.size();
541 for (int i=0; i<N; i++) {
542 Host h = mHosts.get(i);
543 if (h.uid == uid && h.hostId == hostId) {
544 return h;
545 }
546 }
547 return null;
548 }
549
550 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
551 final int N = mHosts.size();
552 for (int i=0; i<N; i++) {
553 Host h = mHosts.get(i);
554 if (h.hostId == hostId && h.packageName.equals(packageName)) {
555 return h;
556 }
557 }
558 Host host = new Host();
559 host.packageName = packageName;
560 host.uid = uid;
561 host.hostId = hostId;
562 mHosts.add(host);
563 return host;
564 }
565
566 void pruneHostLocked(Host host) {
567 if (host.instances.size() == 0 && host.callbacks == null) {
568 mHosts.remove(host);
569 }
570 }
571
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700572 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 PackageManager pm = mPackageManager;
574
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700575 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
577 PackageManager.GET_META_DATA);
578
Bjorn Bringert5f857802010-02-10 23:09:48 +0000579 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 for (int i=0; i<N; i++) {
581 ResolveInfo ri = broadcastReceivers.get(i);
582 addProviderLocked(ri);
583 }
584 }
585
586 boolean addProviderLocked(ResolveInfo ri) {
587 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
588 ri.activityInfo.name), ri);
589 if (p != null) {
590 mInstalledProviders.add(p);
591 return true;
592 } else {
593 return false;
594 }
595 }
596
597 void removeProviderLocked(int index, Provider p) {
598 int N = p.instances.size();
599 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700600 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700602 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 // Stop telling the host about updates for this from now on
604 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700605 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700607 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 id.provider = null;
609 pruneHostLocked(id.host);
610 id.host = null;
611 }
612 p.instances.clear();
613 mInstalledProviders.remove(index);
614 // no need to send the DISABLE broadcast, since the receiver is gone anyway
615 cancelBroadcasts(p);
616 }
617
618 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700619 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 intent.setComponent(p.info.provider);
621 mContext.sendBroadcast(intent);
622 }
623
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700624 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
625 if (appWidgetIds != null && appWidgetIds.length > 0) {
626 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
627 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 intent.setComponent(p.info.provider);
629 mContext.sendBroadcast(intent);
630 }
631 }
632
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700633 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 if (p.info.updatePeriodMillis > 0) {
635 // if this is the first instance, set the alarm. otherwise,
636 // rely on the fact that we've already set it and that
637 // PendingIntent.getBroadcast will update the extras.
638 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700639 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
640 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 intent.setComponent(p.info.provider);
642 long token = Binder.clearCallingIdentity();
643 try {
644 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
645 PendingIntent.FLAG_UPDATE_CURRENT);
646 } finally {
647 Binder.restoreCallingIdentity(token);
648 }
649 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700650 long period = p.info.updatePeriodMillis;
651 if (period < MIN_UPDATE_PERIOD) {
652 period = MIN_UPDATE_PERIOD;
653 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700655 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 }
657 }
658 }
659
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700660 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700662 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700664 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700666 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 }
668
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700669 public int[] getAppWidgetIds(ComponentName provider) {
670 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 Provider p = lookupProviderLocked(provider);
672 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700673 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 } else {
675 return new int[0];
676 }
677 }
678 }
679
680 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
681 Provider p = null;
682
683 ActivityInfo activityInfo = ri.activityInfo;
684 XmlResourceParser parser = null;
685 try {
686 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700687 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800689 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700690 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 return null;
692 }
693
694 AttributeSet attrs = Xml.asAttributeSet(parser);
695
696 int type;
697 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
698 && type != XmlPullParser.START_TAG) {
699 // drain whitespace, comments, etc.
700 }
701
702 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700703 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800704 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700705 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 return null;
707 }
708
709 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700710 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
Romain Guyd2671e12010-03-11 18:06:42 -0800711 // If metaData was null, we would have returned earlier when getting
712 // the parser No need to do the check here
713 info.oldName = activityInfo.metaData.getString(
714 AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715
716 info.provider = component;
717 p.uid = activityInfo.applicationInfo.uid;
718
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800719 Resources res = mPackageManager.getResourcesForApplication(
720 activityInfo.applicationInfo);
721
722 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700723 com.android.internal.R.styleable.AppWidgetProviderInfo);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700724
725 // These dimensions has to be resolved in the application's context.
726 // We simply send back the raw complex data, which will be
727 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
728 TypedValue value = sa.peekValue(
729 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
730 info.minWidth = value != null ? value.data : 0;
731 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
732 info.minHeight = value != null ? value.data : 0;
733
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700735 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700737 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700739 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 if (className != null) {
741 info.configure = new ComponentName(component.getPackageName(), className);
742 }
743 info.label = activityInfo.loadLabel(mPackageManager).toString();
744 info.icon = ri.getIconResource();
745 sa.recycle();
746 } catch (Exception e) {
747 // Ok to catch Exception here, because anything going wrong because
748 // of what a client process passes to us should not be fatal for the
749 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800750 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 return null;
752 } finally {
753 if (parser != null) parser.close();
754 }
755 return p;
756 }
757
758 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
759 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
760 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
761 throw new PackageManager.NameNotFoundException();
762 }
763 return pkgInfo.applicationInfo.uid;
764 }
765
766 int enforceCallingUid(String packageName) throws IllegalArgumentException {
767 int callingUid = getCallingUid();
768 int packageUid;
769 try {
770 packageUid = getUidForPackage(packageName);
771 } catch (PackageManager.NameNotFoundException ex) {
772 throw new IllegalArgumentException("packageName and uid don't match packageName="
773 + packageName);
774 }
Marco Nelissen54796e72009-04-30 15:16:30 -0700775 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 throw new IllegalArgumentException("packageName and uid don't match packageName="
777 + packageName);
778 }
779 return callingUid;
780 }
781
782 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700783 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 final int N = mInstalledProviders.size();
785 for (int i=0; i<N; i++) {
786 Provider p = mInstalledProviders.get(i);
787 if (p.instances.size() > 0) {
788 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700789 int[] appWidgetIds = getAppWidgetIds(p);
790 sendUpdateIntentLocked(p, appWidgetIds);
791 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 }
793 }
794 }
795 }
796
797 // only call from initialization -- it assumes that the data structures are all empty
798 void loadStateLocked() {
799 File temp = savedStateTempFile();
800 File real = savedStateRealFile();
801
802 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
803 // real one. if there is both a real file and a temp one, assume that the temp one isn't
804 // fully written and delete it.
805 if (real.exists()) {
806 readStateFromFileLocked(real);
807 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700808 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 temp.delete();
810 }
811 } else if (temp.exists()) {
812 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -0700813 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 temp.renameTo(real);
815 }
816 }
817
818 void saveStateLocked() {
819 File temp = savedStateTempFile();
820 File real = savedStateRealFile();
821
822 if (!real.exists()) {
823 // If the real one doesn't exist, it's either because this is the first time
824 // or because something went wrong while copying them. In this case, we can't
825 // trust anything that's in temp. In order to have the loadState code not
826 // use the temporary one until it's fully written, create an empty file
827 // for real, which will we'll shortly delete.
828 try {
Romain Guya5475592009-07-01 17:20:08 -0700829 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 real.createNewFile();
831 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -0700832 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 }
834 }
835
836 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700837 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 temp.delete();
839 }
840
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700841 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800842 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700843 return;
844 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845
Romain Guya5475592009-07-01 17:20:08 -0700846 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 real.delete();
Romain Guya5475592009-07-01 17:20:08 -0700848 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 temp.renameTo(real);
850 }
851
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700852 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 FileOutputStream stream = null;
854 int N;
855
856 try {
857 stream = new FileOutputStream(file, false);
858 XmlSerializer out = new FastXmlSerializer();
859 out.setOutput(stream, "utf-8");
860 out.startDocument(null, true);
861
862
863 out.startTag(null, "gs");
864
865 int providerIndex = 0;
866 N = mInstalledProviders.size();
867 for (int i=0; i<N; i++) {
868 Provider p = mInstalledProviders.get(i);
869 if (p.instances.size() > 0) {
870 out.startTag(null, "p");
871 out.attribute(null, "pkg", p.info.provider.getPackageName());
872 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +0800873 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 p.tag = providerIndex;
875 providerIndex++;
876 }
877 }
878
879 N = mHosts.size();
880 for (int i=0; i<N; i++) {
881 Host host = mHosts.get(i);
882 out.startTag(null, "h");
883 out.attribute(null, "pkg", host.packageName);
884 out.attribute(null, "id", Integer.toHexString(host.hostId));
885 out.endTag(null, "h");
886 host.tag = i;
887 }
888
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700889 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700891 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700893 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 out.attribute(null, "h", Integer.toHexString(id.host.tag));
895 if (id.provider != null) {
896 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
897 }
898 out.endTag(null, "g");
899 }
900
901 out.endTag(null, "gs");
902
903 out.endDocument();
904 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700905 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 } catch (IOException e) {
907 try {
908 if (stream != null) {
909 stream.close();
910 }
911 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -0700912 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 }
914 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700915 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 file.delete();
917 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700918 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 }
920 }
921
922 void readStateFromFileLocked(File file) {
923 FileInputStream stream = null;
924
925 boolean success = false;
926
927 try {
928 stream = new FileInputStream(file);
929 XmlPullParser parser = Xml.newPullParser();
930 parser.setInput(stream, null);
931
932 int type;
933 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -0700934 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 do {
936 type = parser.next();
937 if (type == XmlPullParser.START_TAG) {
938 String tag = parser.getName();
939 if ("p".equals(tag)) {
940 // TODO: do we need to check that this package has the same signature
941 // as before?
942 String pkg = parser.getAttributeValue(null, "pkg");
943 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c2010-03-11 15:30:02 -0800944
945 final PackageManager packageManager = mContext.getPackageManager();
946 try {
947 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
948 } catch (PackageManager.NameNotFoundException e) {
949 String[] pkgs = packageManager.currentToCanonicalPackageNames(
950 new String[] { pkg });
951 pkg = pkgs[0];
952 }
953
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
955 if (p == null && mSafeMode) {
956 // if we're in safe mode, make a temporary one
957 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700958 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 p.info.provider = new ComponentName(pkg, cl);
960 p.zombie = true;
961 mInstalledProviders.add(p);
962 }
963 if (p != null) {
964 // if it wasn't uninstalled or something
965 loadedProviders.put(providerIndex, p);
966 }
967 providerIndex++;
968 }
969 else if ("h".equals(tag)) {
970 Host host = new Host();
971
972 // TODO: do we need to check that this package has the same signature
973 // as before?
974 host.packageName = parser.getAttributeValue(null, "pkg");
975 try {
976 host.uid = getUidForPackage(host.packageName);
977 } catch (PackageManager.NameNotFoundException ex) {
978 host.zombie = true;
979 }
980 if (!host.zombie || mSafeMode) {
981 // In safe mode, we don't discard the hosts we don't recognize
982 // so that they're not pruned from our list. Otherwise, we do.
983 host.hostId = Integer.parseInt(
984 parser.getAttributeValue(null, "id"), 16);
985 mHosts.add(host);
986 }
987 }
988 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700989 AppWidgetId id = new AppWidgetId();
990 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
991 if (id.appWidgetId >= mNextAppWidgetId) {
992 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 }
994
995 String providerString = parser.getAttributeValue(null, "p");
996 if (providerString != null) {
997 // there's no provider if it hasn't been bound yet.
998 // maybe we don't have to save this, but it brings the system
999 // to the state it was in.
1000 int pIndex = Integer.parseInt(providerString, 16);
1001 id.provider = loadedProviders.get(pIndex);
1002 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001003 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 + pIndex + " which is " + id.provider);
1005 }
1006 if (id.provider == null) {
1007 // This provider is gone. We just let the host figure out
1008 // that this happened when it fails to load it.
1009 continue;
1010 }
1011 }
1012
1013 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1014 id.host = mHosts.get(hIndex);
1015 if (id.host == null) {
1016 // This host is gone.
1017 continue;
1018 }
1019
1020 if (id.provider != null) {
1021 id.provider.instances.add(id);
1022 }
1023 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001024 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 }
1026 }
1027 } while (type != XmlPullParser.END_DOCUMENT);
1028 success = true;
1029 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001030 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001032 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001034 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001036 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001038 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 }
1040 try {
1041 if (stream != null) {
1042 stream.close();
1043 }
1044 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001045 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 }
1047
1048 if (success) {
1049 // delete any hosts that didn't manage to get connected (should happen)
1050 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001051 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 pruneHostLocked(mHosts.get(i));
1053 }
1054 } else {
1055 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001056 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 mHosts.clear();
1058 final int N = mInstalledProviders.size();
1059 for (int i=0; i<N; i++) {
1060 mInstalledProviders.get(i).instances.clear();
1061 }
1062 }
1063 }
1064
1065 File savedStateTempFile() {
1066 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1067 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1068 }
1069
1070 File savedStateRealFile() {
1071 return new File("/data/system/" + SETTINGS_FILENAME);
1072 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1073 }
1074
1075 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1076 public void onReceive(Context context, Intent intent) {
1077 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001078 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1080 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001081 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1082 Locale revised = Locale.getDefault();
1083 if (revised == null || mLocale == null ||
1084 !(revised.equals(mLocale))) {
1085 mLocale = revised;
1086
1087 synchronized (mAppWidgetIds) {
1088 int N = mInstalledProviders.size();
1089 for (int i=N-1; i>=0; i--) {
1090 Provider p = mInstalledProviders.get(i);
1091 String pkgName = p.info.provider.getPackageName();
1092 updateProvidersForPackageLocked(pkgName);
1093 }
1094 saveStateLocked();
1095 }
1096 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001098 boolean added = false;
1099 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001100 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001101 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1102 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001103 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001104 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1105 added = false;
1106 } else {
1107 Uri uri = intent.getData();
1108 if (uri == null) {
1109 return;
1110 }
1111 String pkgName = uri.getSchemeSpecificPart();
1112 if (pkgName == null) {
1113 return;
1114 }
1115 pkgList = new String[] { pkgName };
1116 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1117 }
1118 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 return;
1120 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001121 if (added) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001122 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001123 Bundle extras = intent.getExtras();
1124 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001125 for (String pkgName : pkgList) {
1126 // The package was just upgraded
1127 updateProvidersForPackageLocked(pkgName);
1128 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129 } else {
1130 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001131 for (String pkgName : pkgList) {
1132 addProvidersForPackageLocked(pkgName);
1133 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 }
1135 saveStateLocked();
1136 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001137 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 Bundle extras = intent.getExtras();
1139 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1140 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1141 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001142 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001143 for (String pkgName : pkgList) {
1144 removeProvidersForPackageLocked(pkgName);
1145 saveStateLocked();
1146 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 }
1148 }
1149 }
1150 }
1151 }
1152 };
1153
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001154 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001155 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001156 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1158 PackageManager.GET_META_DATA);
1159
Bjorn Bringert5f857802010-02-10 23:09:48 +00001160 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161 for (int i=0; i<N; i++) {
1162 ResolveInfo ri = broadcastReceivers.get(i);
1163 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc2010-08-24 17:02:09 -04001164 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1165 continue;
1166 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 if (pkgName.equals(ai.packageName)) {
1168 addProviderLocked(ri);
1169 }
1170 }
1171 }
1172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001174 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001175 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001176 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1178 PackageManager.GET_META_DATA);
1179
1180 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001181 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 for (int i=0; i<N; i++) {
1183 ResolveInfo ri = broadcastReceivers.get(i);
1184 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc2010-08-24 17:02:09 -04001185 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1186 continue;
1187 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 if (pkgName.equals(ai.packageName)) {
1189 ComponentName component = new ComponentName(ai.packageName, ai.name);
1190 Provider p = lookupProviderLocked(component);
1191 if (p == null) {
1192 if (addProviderLocked(ri)) {
1193 keep.add(ai.name);
1194 }
1195 } else {
1196 Provider parsed = parseProviderInfoXml(component, ri);
1197 if (parsed != null) {
1198 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001199 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 p.info = parsed.info;
1201 // If it's enabled
1202 final int M = p.instances.size();
1203 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001204 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001205 // Reschedule for the new updatePeriodMillis (don't worry about handling
1206 // it specially if updatePeriodMillis didn't change because we just sent
1207 // an update, and the next one will be updatePeriodMillis from now).
1208 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001209 registerForBroadcastsLocked(p, appWidgetIds);
1210 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001211 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001212 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001213 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 if (id.host != null && id.host.callbacks != null) {
1215 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001216 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217 } catch (RemoteException ex) {
1218 // It failed; remove the callback. No need to prune because
1219 // we know that this host is still referenced by this
1220 // instance.
1221 id.host.callbacks = null;
1222 }
1223 }
1224 }
1225 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001226 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001227 }
1228 }
1229 }
1230 }
1231 }
1232
1233 // prune the ones we don't want to keep
1234 N = mInstalledProviders.size();
1235 for (int i=N-1; i>=0; i--) {
1236 Provider p = mInstalledProviders.get(i);
1237 if (pkgName.equals(p.info.provider.getPackageName())
1238 && !keep.contains(p.info.provider.getClassName())) {
1239 removeProviderLocked(i, p);
1240 }
1241 }
1242 }
1243
1244 void removeProvidersForPackageLocked(String pkgName) {
1245 int N = mInstalledProviders.size();
1246 for (int i=N-1; i>=0; i--) {
1247 Provider p = mInstalledProviders.get(i);
1248 if (pkgName.equals(p.info.provider.getPackageName())) {
1249 removeProviderLocked(i, p);
1250 }
1251 }
1252
1253 // Delete the hosts for this package too
1254 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001255 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 // so we don't need to worry about sending DISABLE broadcasts to them.
1257 N = mHosts.size();
1258 for (int i=N-1; i>=0; i--) {
1259 Host host = mHosts.get(i);
1260 if (pkgName.equals(host.packageName)) {
1261 deleteHostLocked(host);
1262 }
1263 }
1264 }
1265}
1266