blob: 981cc937ba2ffd3987b6f390034328d134626075 [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;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageInfo;
31import android.content.pm.ResolveInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.res.TypedArray;
33import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.net.Uri;
35import android.os.Binder;
36import android.os.Bundle;
Marco Nelissen54796e72009-04-30 15:16:30 -070037import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.os.RemoteException;
39import android.os.SystemClock;
40import android.util.AttributeSet;
41import android.util.Log;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070042import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.util.Xml;
44import android.widget.RemoteViews;
45
46import java.io.IOException;
47import java.io.File;
48import java.io.FileDescriptor;
49import java.io.FileInputStream;
50import java.io.FileOutputStream;
51import java.io.PrintWriter;
52import java.util.ArrayList;
53import java.util.List;
Eric Fischer63c2d9e2009-10-22 15:22:50 -070054import java.util.Locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import java.util.HashMap;
56import java.util.HashSet;
57
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070058import com.android.internal.appwidget.IAppWidgetService;
59import com.android.internal.appwidget.IAppWidgetHost;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import com.android.internal.util.FastXmlSerializer;
61
62import org.xmlpull.v1.XmlPullParser;
63import org.xmlpull.v1.XmlPullParserException;
64import org.xmlpull.v1.XmlSerializer;
65
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070066class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070068 private static final String TAG = "AppWidgetService";
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 SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070072 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
74 /*
75 * When identifying a Host or Provider based on the calling process, use the uid field.
76 * When identifying a Host or Provider based on a package manager broadcast, use the
77 * package given.
78 */
79
80 static class Provider {
81 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070082 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070083 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 PendingIntent broadcast;
85 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
86
87 int tag; // for use while saving state (the index)
88 }
89
90 static class Host {
91 int uid;
92 int hostId;
93 String packageName;
Romain Guya5475592009-07-01 17:20:08 -070094 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070095 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 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
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700101 static class AppWidgetId {
102 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 Provider provider;
104 RemoteViews views;
105 Host host;
106 }
107
108 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700109 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 PackageManager mPackageManager;
111 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700112 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700113 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700114 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
115 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 boolean mSafeMode;
117
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700118 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 mContext = context;
120 mPackageManager = context.getPackageManager();
121 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
122 }
123
124 public void systemReady(boolean safeMode) {
125 mSafeMode = safeMode;
126
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700127 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 loadStateLocked();
129
130 // Register for the boot completed broadcast, so we can send the
131 // ENABLE broacasts. If we try to send them now, they time out,
132 // because the system isn't ready to handle them yet.
133 mContext.registerReceiver(mBroadcastReceiver,
134 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
135
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700136 // Register for configuration changes so we can update the names
137 // of the widgets when the locale changes.
138 mContext.registerReceiver(mBroadcastReceiver,
139 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 // Register for broadcasts about package install, etc., so we can
142 // update the provider list.
143 IntentFilter filter = new IntentFilter();
144 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
145 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
146 filter.addDataScheme("package");
147 mContext.registerReceiver(mBroadcastReceiver, filter);
148 }
149
150 @Override
151 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
152 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
153 != PackageManager.PERMISSION_GRANTED) {
154 pw.println("Permission Denial: can't dump from from pid="
155 + Binder.getCallingPid()
156 + ", uid=" + Binder.getCallingUid());
157 return;
158 }
159
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700160 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700162 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 for (int i=0; i<N; i++) {
164 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700165 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700166 pw.print(" ["); pw.print(i); pw.print("] provider ");
167 pw.print(info.provider.flattenToShortString());
168 pw.println(':');
169 pw.print(" min=("); pw.print(info.minWidth);
170 pw.print("x"); pw.print(info.minHeight);
171 pw.print(") updatePeriodMillis=");
172 pw.print(info.updatePeriodMillis);
173 pw.print(" initialLayout=#");
174 pw.print(Integer.toHexString(info.initialLayout));
175 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 }
177
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700178 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700179 pw.println(" ");
180 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700182 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700183 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700184 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700185 pw.print(" hostId=");
186 pw.print(id.host.hostId); pw.print(' ');
187 pw.print(id.host.packageName); pw.print('/');
188 pw.println(id.host.uid);
189 if (id.provider != null) {
190 pw.print(" provider=");
191 pw.println(id.provider.info.provider.flattenToShortString());
192 }
193 if (id.host != null) {
194 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
195 }
196 if (id.views != null) {
197 pw.print(" views="); pw.println(id.views);
198 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 }
200
201 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700202 pw.println(" ");
203 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 for (int i=0; i<N; i++) {
205 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700206 pw.print(" ["); pw.print(i); pw.print("] hostId=");
207 pw.print(host.hostId); pw.print(' ');
208 pw.print(host.packageName); pw.print('/');
209 pw.print(host.uid); pw.println(':');
210 pw.print(" callbacks="); pw.println(host.callbacks);
211 pw.print(" instances.size="); pw.print(host.instances.size());
212 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 }
214 }
215 }
216
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700217 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700219 synchronized (mAppWidgetIds) {
220 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221
222 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
223
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700224 AppWidgetId id = new AppWidgetId();
225 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 id.host = host;
227
228 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700229 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230
231 saveStateLocked();
232
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700233 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 }
235 }
236
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700237 public void deleteAppWidgetId(int appWidgetId) {
238 synchronized (mAppWidgetIds) {
239 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700241 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 saveStateLocked();
243 }
244 }
245 }
246
247 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700248 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 int callingUid = getCallingUid();
250 Host host = lookupHostLocked(callingUid, hostId);
251 if (host != null) {
252 deleteHostLocked(host);
253 saveStateLocked();
254 }
255 }
256 }
257
258 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700259 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 int callingUid = getCallingUid();
261 final int N = mHosts.size();
262 boolean changed = false;
263 for (int i=N-1; i>=0; i--) {
264 Host host = mHosts.get(i);
265 if (host.uid == callingUid) {
266 deleteHostLocked(host);
267 changed = true;
268 }
269 }
270 if (changed) {
271 saveStateLocked();
272 }
273 }
274 }
275
276 void deleteHostLocked(Host host) {
277 final int N = host.instances.size();
278 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700279 AppWidgetId id = host.instances.get(i);
280 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 }
282 host.instances.clear();
283 mHosts.remove(host);
284 // it's gone or going away, abruptly drop the callback connection
285 host.callbacks = null;
286 }
287
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700288 void deleteAppWidgetLocked(AppWidgetId id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 Host host = id.host;
290 host.instances.remove(id);
291 pruneHostLocked(host);
292
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700293 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294
295 Provider p = id.provider;
296 if (p != null) {
297 p.instances.remove(id);
298 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700299 // send the broacast saying that this appWidgetId has been deleted
300 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700302 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 mContext.sendBroadcast(intent);
304 if (p.instances.size() == 0) {
305 // cancel the future updates
306 cancelBroadcasts(p);
307
308 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700309 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 intent.setComponent(p.info.provider);
311 mContext.sendBroadcast(intent);
312 }
313 }
314 }
315 }
316
317 void cancelBroadcasts(Provider p) {
318 if (p.broadcast != null) {
319 mAlarmManager.cancel(p.broadcast);
320 long token = Binder.clearCallingIdentity();
321 try {
322 p.broadcast.cancel();
323 } finally {
324 Binder.restoreCallingIdentity(token);
325 }
326 p.broadcast = null;
327 }
328 }
329
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700330 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
331 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
332 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
333 synchronized (mAppWidgetIds) {
334 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700336 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 }
338 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700339 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 + id.provider.info.provider);
341 }
342 Provider p = lookupProviderLocked(provider);
343 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700344 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 }
346 if (p.zombie) {
347 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
348 + " safe mode: " + provider);
349 }
350
351 id.provider = p;
352 p.instances.add(id);
353 int instancesSize = p.instances.size();
354 if (instancesSize == 1) {
355 // tell the provider that it's ready
356 sendEnableIntentLocked(p);
357 }
358
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700359 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 // It's less critical when the next one happens, so when we schdule the next one,
361 // we add updatePeriodMillis to its start time. That time will have some slop,
362 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700363 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364
365 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700366 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 saveStateLocked();
368 }
369 }
370
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700371 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
372 synchronized (mAppWidgetIds) {
373 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 if (id != null && id.provider != null && !id.provider.zombie) {
375 return id.provider.info;
376 }
377 return null;
378 }
379 }
380
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700381 public RemoteViews getAppWidgetViews(int appWidgetId) {
382 synchronized (mAppWidgetIds) {
383 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 if (id != null) {
385 return id.views;
386 }
387 return null;
388 }
389 }
390
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700391 public List<AppWidgetProviderInfo> getInstalledProviders() {
392 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700394 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 for (int i=0; i<N; i++) {
396 Provider p = mInstalledProviders.get(i);
397 if (!p.zombie) {
398 result.add(p.info);
399 }
400 }
401 return result;
402 }
403 }
404
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700405 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
406 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 return;
408 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700409 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 return;
411 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700412 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700414 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700416 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
417 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 }
419 }
420 }
421
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700422 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
423 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 Provider p = lookupProviderLocked(provider);
425 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700426 Log.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 return;
428 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700429 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 final int N = instances.size();
431 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700432 AppWidgetId id = instances.get(i);
433 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 }
435 }
436 }
437
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700438 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
439 // allow for stale appWidgetIds and other badness
440 // lookup also checks that the calling process can access the appWidgetId
441 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
443 id.views = views;
444
445 // is anyone listening?
446 if (id.host.callbacks != null) {
447 try {
448 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700449 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 } catch (RemoteException e) {
451 // It failed; remove the callback. No need to prune because
452 // we know that this host is still referenced by this instance.
453 id.host.callbacks = null;
454 }
455 }
456 }
457 }
458
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700459 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 List<RemoteViews> updatedViews) {
461 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700462 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
464 host.callbacks = callbacks;
465
466 updatedViews.clear();
467
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700468 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 int N = instances.size();
470 int[] updatedIds = new int[N];
471 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700472 AppWidgetId id = instances.get(i);
473 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 updatedViews.add(id.views);
475 }
476 return updatedIds;
477 }
478 }
479
480 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700481 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700483 if (host != null) {
484 host.callbacks = null;
485 pruneHostLocked(host);
486 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 }
488 }
489
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700490 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700492 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 return true;
494 }
495 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700496 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 return true;
498 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700499 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700501 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 return true;
503 }
504 // Nobody else can access it.
505 return false;
506 }
507
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700508 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700510 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700512 AppWidgetId id = mAppWidgetIds.get(i);
513 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 return id;
515 }
516 }
517 return null;
518 }
519
520 Provider lookupProviderLocked(ComponentName provider) {
521 final int N = mInstalledProviders.size();
522 for (int i=0; i<N; i++) {
523 Provider p = mInstalledProviders.get(i);
524 if (p.info.provider.equals(provider)) {
525 return p;
526 }
527 }
528 return null;
529 }
530
531 Host lookupHostLocked(int uid, int hostId) {
532 final int N = mHosts.size();
533 for (int i=0; i<N; i++) {
534 Host h = mHosts.get(i);
535 if (h.uid == uid && h.hostId == hostId) {
536 return h;
537 }
538 }
539 return null;
540 }
541
542 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
543 final int N = mHosts.size();
544 for (int i=0; i<N; i++) {
545 Host h = mHosts.get(i);
546 if (h.hostId == hostId && h.packageName.equals(packageName)) {
547 return h;
548 }
549 }
550 Host host = new Host();
551 host.packageName = packageName;
552 host.uid = uid;
553 host.hostId = hostId;
554 mHosts.add(host);
555 return host;
556 }
557
558 void pruneHostLocked(Host host) {
559 if (host.instances.size() == 0 && host.callbacks == null) {
560 mHosts.remove(host);
561 }
562 }
563
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700564 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 PackageManager pm = mPackageManager;
566
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700567 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
569 PackageManager.GET_META_DATA);
570
571 final int N = broadcastReceivers.size();
572 for (int i=0; i<N; i++) {
573 ResolveInfo ri = broadcastReceivers.get(i);
574 addProviderLocked(ri);
575 }
576 }
577
578 boolean addProviderLocked(ResolveInfo ri) {
579 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
580 ri.activityInfo.name), ri);
581 if (p != null) {
582 mInstalledProviders.add(p);
583 return true;
584 } else {
585 return false;
586 }
587 }
588
589 void removeProviderLocked(int index, Provider p) {
590 int N = p.instances.size();
591 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700592 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700594 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 // Stop telling the host about updates for this from now on
596 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700597 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700599 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 id.provider = null;
601 pruneHostLocked(id.host);
602 id.host = null;
603 }
604 p.instances.clear();
605 mInstalledProviders.remove(index);
606 // no need to send the DISABLE broadcast, since the receiver is gone anyway
607 cancelBroadcasts(p);
608 }
609
610 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700611 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 intent.setComponent(p.info.provider);
613 mContext.sendBroadcast(intent);
614 }
615
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700616 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
617 if (appWidgetIds != null && appWidgetIds.length > 0) {
618 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
619 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 intent.setComponent(p.info.provider);
621 mContext.sendBroadcast(intent);
622 }
623 }
624
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700625 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 if (p.info.updatePeriodMillis > 0) {
627 // if this is the first instance, set the alarm. otherwise,
628 // rely on the fact that we've already set it and that
629 // PendingIntent.getBroadcast will update the extras.
630 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700631 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
632 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 intent.setComponent(p.info.provider);
634 long token = Binder.clearCallingIdentity();
635 try {
636 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
637 PendingIntent.FLAG_UPDATE_CURRENT);
638 } finally {
639 Binder.restoreCallingIdentity(token);
640 }
641 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700642 long period = p.info.updatePeriodMillis;
643 if (period < MIN_UPDATE_PERIOD) {
644 period = MIN_UPDATE_PERIOD;
645 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700647 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648 }
649 }
650 }
651
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700652 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700654 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700656 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700658 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 }
660
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700661 public int[] getAppWidgetIds(ComponentName provider) {
662 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 Provider p = lookupProviderLocked(provider);
664 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700665 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 } else {
667 return new int[0];
668 }
669 }
670 }
671
672 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
673 Provider p = null;
674
675 ActivityInfo activityInfo = ri.activityInfo;
676 XmlResourceParser parser = null;
677 try {
678 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700679 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 if (parser == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700681 Log.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
682 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 return null;
684 }
685
686 AttributeSet attrs = Xml.asAttributeSet(parser);
687
688 int type;
689 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
690 && type != XmlPullParser.START_TAG) {
691 // drain whitespace, comments, etc.
692 }
693
694 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700695 if (!"appwidget-provider".equals(nodeName)) {
696 Log.w(TAG, "Meta-data does not start with appwidget-provider tag for"
697 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 return null;
699 }
700
701 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700702 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703
704 info.provider = component;
705 p.uid = activityInfo.applicationInfo.uid;
706
707 TypedArray sa = mContext.getResources().obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700708 com.android.internal.R.styleable.AppWidgetProviderInfo);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700709
710 // These dimensions has to be resolved in the application's context.
711 // We simply send back the raw complex data, which will be
712 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
713 TypedValue value = sa.peekValue(
714 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
715 info.minWidth = value != null ? value.data : 0;
716 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
717 info.minHeight = value != null ? value.data : 0;
718
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700720 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700722 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700724 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 if (className != null) {
726 info.configure = new ComponentName(component.getPackageName(), className);
727 }
728 info.label = activityInfo.loadLabel(mPackageManager).toString();
729 info.icon = ri.getIconResource();
730 sa.recycle();
731 } catch (Exception e) {
732 // Ok to catch Exception here, because anything going wrong because
733 // of what a client process passes to us should not be fatal for the
734 // system process.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700735 Log.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 return null;
737 } finally {
738 if (parser != null) parser.close();
739 }
740 return p;
741 }
742
743 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
744 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
745 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
746 throw new PackageManager.NameNotFoundException();
747 }
748 return pkgInfo.applicationInfo.uid;
749 }
750
751 int enforceCallingUid(String packageName) throws IllegalArgumentException {
752 int callingUid = getCallingUid();
753 int packageUid;
754 try {
755 packageUid = getUidForPackage(packageName);
756 } catch (PackageManager.NameNotFoundException ex) {
757 throw new IllegalArgumentException("packageName and uid don't match packageName="
758 + packageName);
759 }
Marco Nelissen54796e72009-04-30 15:16:30 -0700760 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 throw new IllegalArgumentException("packageName and uid don't match packageName="
762 + packageName);
763 }
764 return callingUid;
765 }
766
767 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700768 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 final int N = mInstalledProviders.size();
770 for (int i=0; i<N; i++) {
771 Provider p = mInstalledProviders.get(i);
772 if (p.instances.size() > 0) {
773 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700774 int[] appWidgetIds = getAppWidgetIds(p);
775 sendUpdateIntentLocked(p, appWidgetIds);
776 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 }
778 }
779 }
780 }
781
782 // only call from initialization -- it assumes that the data structures are all empty
783 void loadStateLocked() {
784 File temp = savedStateTempFile();
785 File real = savedStateRealFile();
786
787 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
788 // real one. if there is both a real file and a temp one, assume that the temp one isn't
789 // fully written and delete it.
790 if (real.exists()) {
791 readStateFromFileLocked(real);
792 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700793 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 temp.delete();
795 }
796 } else if (temp.exists()) {
797 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -0700798 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 temp.renameTo(real);
800 }
801 }
802
803 void saveStateLocked() {
804 File temp = savedStateTempFile();
805 File real = savedStateRealFile();
806
807 if (!real.exists()) {
808 // If the real one doesn't exist, it's either because this is the first time
809 // or because something went wrong while copying them. In this case, we can't
810 // trust anything that's in temp. In order to have the loadState code not
811 // use the temporary one until it's fully written, create an empty file
812 // for real, which will we'll shortly delete.
813 try {
Romain Guya5475592009-07-01 17:20:08 -0700814 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 real.createNewFile();
816 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -0700817 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 }
819 }
820
821 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700822 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 temp.delete();
824 }
825
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700826 if (!writeStateToFileLocked(temp)) {
827 Log.w(TAG, "Failed to persist new settings");
828 return;
829 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830
Romain Guya5475592009-07-01 17:20:08 -0700831 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 real.delete();
Romain Guya5475592009-07-01 17:20:08 -0700833 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 temp.renameTo(real);
835 }
836
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700837 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 FileOutputStream stream = null;
839 int N;
840
841 try {
842 stream = new FileOutputStream(file, false);
843 XmlSerializer out = new FastXmlSerializer();
844 out.setOutput(stream, "utf-8");
845 out.startDocument(null, true);
846
847
848 out.startTag(null, "gs");
849
850 int providerIndex = 0;
851 N = mInstalledProviders.size();
852 for (int i=0; i<N; i++) {
853 Provider p = mInstalledProviders.get(i);
854 if (p.instances.size() > 0) {
855 out.startTag(null, "p");
856 out.attribute(null, "pkg", p.info.provider.getPackageName());
857 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +0800858 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 p.tag = providerIndex;
860 providerIndex++;
861 }
862 }
863
864 N = mHosts.size();
865 for (int i=0; i<N; i++) {
866 Host host = mHosts.get(i);
867 out.startTag(null, "h");
868 out.attribute(null, "pkg", host.packageName);
869 out.attribute(null, "id", Integer.toHexString(host.hostId));
870 out.endTag(null, "h");
871 host.tag = i;
872 }
873
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700874 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700876 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700878 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 out.attribute(null, "h", Integer.toHexString(id.host.tag));
880 if (id.provider != null) {
881 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
882 }
883 out.endTag(null, "g");
884 }
885
886 out.endTag(null, "gs");
887
888 out.endDocument();
889 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700890 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 } catch (IOException e) {
892 try {
893 if (stream != null) {
894 stream.close();
895 }
896 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -0700897 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 }
899 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700900 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 file.delete();
902 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700903 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 }
905 }
906
907 void readStateFromFileLocked(File file) {
908 FileInputStream stream = null;
909
910 boolean success = false;
911
912 try {
913 stream = new FileInputStream(file);
914 XmlPullParser parser = Xml.newPullParser();
915 parser.setInput(stream, null);
916
917 int type;
918 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -0700919 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 do {
921 type = parser.next();
922 if (type == XmlPullParser.START_TAG) {
923 String tag = parser.getName();
924 if ("p".equals(tag)) {
925 // TODO: do we need to check that this package has the same signature
926 // as before?
927 String pkg = parser.getAttributeValue(null, "pkg");
928 String cl = parser.getAttributeValue(null, "cl");
929 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
930 if (p == null && mSafeMode) {
931 // if we're in safe mode, make a temporary one
932 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700933 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 p.info.provider = new ComponentName(pkg, cl);
935 p.zombie = true;
936 mInstalledProviders.add(p);
937 }
938 if (p != null) {
939 // if it wasn't uninstalled or something
940 loadedProviders.put(providerIndex, p);
941 }
942 providerIndex++;
943 }
944 else if ("h".equals(tag)) {
945 Host host = new Host();
946
947 // TODO: do we need to check that this package has the same signature
948 // as before?
949 host.packageName = parser.getAttributeValue(null, "pkg");
950 try {
951 host.uid = getUidForPackage(host.packageName);
952 } catch (PackageManager.NameNotFoundException ex) {
953 host.zombie = true;
954 }
955 if (!host.zombie || mSafeMode) {
956 // In safe mode, we don't discard the hosts we don't recognize
957 // so that they're not pruned from our list. Otherwise, we do.
958 host.hostId = Integer.parseInt(
959 parser.getAttributeValue(null, "id"), 16);
960 mHosts.add(host);
961 }
962 }
963 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700964 AppWidgetId id = new AppWidgetId();
965 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
966 if (id.appWidgetId >= mNextAppWidgetId) {
967 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 }
969
970 String providerString = parser.getAttributeValue(null, "p");
971 if (providerString != null) {
972 // there's no provider if it hasn't been bound yet.
973 // maybe we don't have to save this, but it brings the system
974 // to the state it was in.
975 int pIndex = Integer.parseInt(providerString, 16);
976 id.provider = loadedProviders.get(pIndex);
977 if (false) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700978 Log.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 + pIndex + " which is " + id.provider);
980 }
981 if (id.provider == null) {
982 // This provider is gone. We just let the host figure out
983 // that this happened when it fails to load it.
984 continue;
985 }
986 }
987
988 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
989 id.host = mHosts.get(hIndex);
990 if (id.host == null) {
991 // This host is gone.
992 continue;
993 }
994
995 if (id.provider != null) {
996 id.provider.instances.add(id);
997 }
998 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700999 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 }
1001 }
1002 } while (type != XmlPullParser.END_DOCUMENT);
1003 success = true;
1004 } catch (NullPointerException e) {
1005 Log.w(TAG, "failed parsing " + file, e);
1006 } catch (NumberFormatException e) {
1007 Log.w(TAG, "failed parsing " + file, e);
1008 } catch (XmlPullParserException e) {
1009 Log.w(TAG, "failed parsing " + file, e);
1010 } catch (IOException e) {
1011 Log.w(TAG, "failed parsing " + file, e);
1012 } catch (IndexOutOfBoundsException e) {
1013 Log.w(TAG, "failed parsing " + file, e);
1014 }
1015 try {
1016 if (stream != null) {
1017 stream.close();
1018 }
1019 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001020 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 }
1022
1023 if (success) {
1024 // delete any hosts that didn't manage to get connected (should happen)
1025 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001026 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 pruneHostLocked(mHosts.get(i));
1028 }
1029 } else {
1030 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001031 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 mHosts.clear();
1033 final int N = mInstalledProviders.size();
1034 for (int i=0; i<N; i++) {
1035 mInstalledProviders.get(i).instances.clear();
1036 }
1037 }
1038 }
1039
1040 File savedStateTempFile() {
1041 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1042 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1043 }
1044
1045 File savedStateRealFile() {
1046 return new File("/data/system/" + SETTINGS_FILENAME);
1047 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1048 }
1049
1050 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1051 public void onReceive(Context context, Intent intent) {
1052 String action = intent.getAction();
1053 //Log.d(TAG, "received " + action);
1054 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1055 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001056 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1057 Locale revised = Locale.getDefault();
1058 if (revised == null || mLocale == null ||
1059 !(revised.equals(mLocale))) {
1060 mLocale = revised;
1061
1062 synchronized (mAppWidgetIds) {
1063 int N = mInstalledProviders.size();
1064 for (int i=N-1; i>=0; i--) {
1065 Provider p = mInstalledProviders.get(i);
1066 String pkgName = p.info.provider.getPackageName();
1067 updateProvidersForPackageLocked(pkgName);
1068 }
1069 saveStateLocked();
1070 }
1071 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 } else {
1073 Uri uri = intent.getData();
1074 if (uri == null) {
1075 return;
1076 }
1077 String pkgName = uri.getSchemeSpecificPart();
1078 if (pkgName == null) {
1079 return;
1080 }
1081
1082 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001083 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 Bundle extras = intent.getExtras();
1085 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1086 // The package was just upgraded
1087 updateProvidersForPackageLocked(pkgName);
1088 } else {
1089 // The package was just added
1090 addProvidersForPackageLocked(pkgName);
1091 }
1092 saveStateLocked();
1093 }
1094 }
1095 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
1096 Bundle extras = intent.getExtras();
1097 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1098 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1099 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001100 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 removeProvidersForPackageLocked(pkgName);
1102 saveStateLocked();
1103 }
1104 }
1105 }
1106 }
1107 }
1108 };
1109
1110 // TODO: If there's a better way of matching an intent filter against the
1111 // packages for a given package, use that.
1112 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001113 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1115 PackageManager.GET_META_DATA);
1116
1117 final int N = broadcastReceivers.size();
1118 for (int i=0; i<N; i++) {
1119 ResolveInfo ri = broadcastReceivers.get(i);
1120 ActivityInfo ai = ri.activityInfo;
1121
1122 if (pkgName.equals(ai.packageName)) {
1123 addProviderLocked(ri);
1124 }
1125 }
1126 }
1127
1128 // TODO: If there's a better way of matching an intent filter against the
1129 // packages for a given package, use that.
1130 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001131 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001132 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1134 PackageManager.GET_META_DATA);
1135
1136 // add the missing ones and collect which ones to keep
1137 int N = broadcastReceivers.size();
1138 for (int i=0; i<N; i++) {
1139 ResolveInfo ri = broadcastReceivers.get(i);
1140 ActivityInfo ai = ri.activityInfo;
1141 if (pkgName.equals(ai.packageName)) {
1142 ComponentName component = new ComponentName(ai.packageName, ai.name);
1143 Provider p = lookupProviderLocked(component);
1144 if (p == null) {
1145 if (addProviderLocked(ri)) {
1146 keep.add(ai.name);
1147 }
1148 } else {
1149 Provider parsed = parseProviderInfoXml(component, ri);
1150 if (parsed != null) {
1151 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001152 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 p.info = parsed.info;
1154 // If it's enabled
1155 final int M = p.instances.size();
1156 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001157 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 // Reschedule for the new updatePeriodMillis (don't worry about handling
1159 // it specially if updatePeriodMillis didn't change because we just sent
1160 // an update, and the next one will be updatePeriodMillis from now).
1161 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001162 registerForBroadcastsLocked(p, appWidgetIds);
1163 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001165 AppWidgetId id = p.instances.get(j);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 if (id.host != null && id.host.callbacks != null) {
1167 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001168 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 } catch (RemoteException ex) {
1170 // It failed; remove the callback. No need to prune because
1171 // we know that this host is still referenced by this
1172 // instance.
1173 id.host.callbacks = null;
1174 }
1175 }
1176 }
1177 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001178 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 }
1180 }
1181 }
1182 }
1183 }
1184
1185 // prune the ones we don't want to keep
1186 N = mInstalledProviders.size();
1187 for (int i=N-1; i>=0; i--) {
1188 Provider p = mInstalledProviders.get(i);
1189 if (pkgName.equals(p.info.provider.getPackageName())
1190 && !keep.contains(p.info.provider.getClassName())) {
1191 removeProviderLocked(i, p);
1192 }
1193 }
1194 }
1195
1196 void removeProvidersForPackageLocked(String pkgName) {
1197 int N = mInstalledProviders.size();
1198 for (int i=N-1; i>=0; i--) {
1199 Provider p = mInstalledProviders.get(i);
1200 if (pkgName.equals(p.info.provider.getPackageName())) {
1201 removeProviderLocked(i, p);
1202 }
1203 }
1204
1205 // Delete the hosts for this package too
1206 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001207 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 // so we don't need to worry about sending DISABLE broadcasts to them.
1209 N = mHosts.size();
1210 for (int i=N-1; i>=0; i--) {
1211 Host host = mHosts.get(i);
1212 if (pkgName.equals(host.packageName)) {
1213 deleteHostLocked(host);
1214 }
1215 }
1216 }
1217}
1218