blob: 50f918b02d0d89503ebf10a9cfa3c65a978af486 [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
Winson Chung81f39eb2011-01-11 18:05:01 -080019import java.io.File;
20import java.io.FileDescriptor;
21import java.io.FileInputStream;
22import java.io.FileOutputStream;
23import java.io.IOException;
24import java.io.PrintWriter;
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.HashSet;
28import java.util.Iterator;
29import java.util.List;
30import java.util.Locale;
31
32import org.xmlpull.v1.XmlPullParser;
33import org.xmlpull.v1.XmlPullParserException;
34import org.xmlpull.v1.XmlSerializer;
35
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.app.AlarmManager;
37import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070038import android.appwidget.AppWidgetManager;
39import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.content.BroadcastReceiver;
41import android.content.ComponentName;
42import android.content.Context;
43import android.content.Intent;
Winson Chung81f39eb2011-01-11 18:05:01 -080044import android.content.Intent.FilterComparison;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.content.IntentFilter;
Winson Chung81f39eb2011-01-11 18:05:01 -080046import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.content.pm.ActivityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -040048import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.content.pm.PackageInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080050import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.content.pm.ResolveInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080052import android.content.pm.ServiceInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080053import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.content.res.TypedArray;
55import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.net.Uri;
57import android.os.Binder;
58import android.os.Bundle;
Winson Chung81f39eb2011-01-11 18:05:01 -080059import android.os.IBinder;
Marco Nelissen54796e72009-04-30 15:16:30 -070060import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.os.RemoteException;
62import android.os.SystemClock;
63import android.util.AttributeSet;
Winson Chung16c8d8a2011-01-20 16:19:33 -080064import android.util.Log;
Winson Chung81f39eb2011-01-11 18:05:01 -080065import android.util.Pair;
Joe Onorato8a9b2202010-02-26 18:56:32 -080066import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070067import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.util.Xml;
69import android.widget.RemoteViews;
Winson Chung84bbb022011-02-21 13:57:45 -080070import android.widget.RemoteViewsService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070072import com.android.internal.appwidget.IAppWidgetHost;
Winson Chung81f39eb2011-01-11 18:05:01 -080073import com.android.internal.appwidget.IAppWidgetService;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080074import com.android.internal.util.FastXmlSerializer;
Winson Chung81f39eb2011-01-11 18:05:01 -080075import com.android.internal.widget.IRemoteViewsAdapterConnection;
Winson Chung84bbb022011-02-21 13:57:45 -080076import com.android.internal.widget.IRemoteViewsFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070078class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070080 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070082 private static final String SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070084 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085
86 /*
87 * When identifying a Host or Provider based on the calling process, use the uid field.
88 * When identifying a Host or Provider based on a package manager broadcast, use the
89 * package given.
90 */
91
92 static class Provider {
93 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070094 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070095 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 PendingIntent broadcast;
97 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
98
99 int tag; // for use while saving state (the index)
100 }
101
102 static class Host {
103 int uid;
104 int hostId;
105 String packageName;
Romain Guya5475592009-07-01 17:20:08 -0700106 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700107 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
109
110 int tag; // for use while saving state (the index)
111 }
112
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700113 static class AppWidgetId {
114 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 Provider provider;
116 RemoteViews views;
117 Host host;
118 }
119
Winson Chung81f39eb2011-01-11 18:05:01 -0800120 /**
121 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
122 * This needs to be a static inner class since a reference to the ServiceConnection is held
123 * globally and may lead us to leak AppWidgetService instances (if there were more than one).
124 */
125 static class ServiceConnectionProxy implements ServiceConnection {
Winson Chung81f39eb2011-01-11 18:05:01 -0800126 private final Pair<Integer, Intent.FilterComparison> mKey;
127 private final IBinder mConnectionCb;
128
Winson Chung16c8d8a2011-01-20 16:19:33 -0800129 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800130 mKey = key;
131 mConnectionCb = connectionCb;
132 }
133 public void onServiceConnected(ComponentName name, IBinder service) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800134 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800135 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
136 try {
137 cb.onServiceConnected(service);
138 } catch (RemoteException e) {
139 e.printStackTrace();
140 }
141 }
142 public void onServiceDisconnected(ComponentName name) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800143 disconnect();
144 }
145 public void disconnect() {
146 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800147 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
148 try {
149 cb.onServiceDisconnected();
Winson Chung81f39eb2011-01-11 18:05:01 -0800150 } catch (RemoteException e) {
151 e.printStackTrace();
152 }
153 }
154 }
155
Winson Chung84bbb022011-02-21 13:57:45 -0800156 // Manages active connections to RemoteViewsServices
Winson Chung81f39eb2011-01-11 18:05:01 -0800157 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
158 mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
Winson Chung84bbb022011-02-21 13:57:45 -0800159 // Manages persistent references to RemoteViewsServices from different App Widgets
160 private final HashMap<FilterComparison, HashSet<Integer>>
161 mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
Winson Chung81f39eb2011-01-11 18:05:01 -0800162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700164 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 PackageManager mPackageManager;
166 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700167 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700168 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700169 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
170 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 boolean mSafeMode;
172
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700173 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 mContext = context;
175 mPackageManager = context.getPackageManager();
176 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
177 }
178
179 public void systemReady(boolean safeMode) {
180 mSafeMode = safeMode;
181
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700182 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 loadStateLocked();
184
185 // Register for the boot completed broadcast, so we can send the
186 // ENABLE broacasts. If we try to send them now, they time out,
187 // because the system isn't ready to handle them yet.
188 mContext.registerReceiver(mBroadcastReceiver,
189 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
190
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700191 // Register for configuration changes so we can update the names
192 // of the widgets when the locale changes.
193 mContext.registerReceiver(mBroadcastReceiver,
194 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 // Register for broadcasts about package install, etc., so we can
197 // update the provider list.
198 IntentFilter filter = new IntentFilter();
199 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
Joe Onoratod070e892011-01-07 20:50:37 -0800200 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
202 filter.addDataScheme("package");
203 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800204 // Register for events related to sdcard installation.
205 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800206 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
207 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800208 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 }
210
211 @Override
212 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
213 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
214 != PackageManager.PERMISSION_GRANTED) {
215 pw.println("Permission Denial: can't dump from from pid="
216 + Binder.getCallingPid()
217 + ", uid=" + Binder.getCallingUid());
218 return;
219 }
220
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700221 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700223 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 for (int i=0; i<N; i++) {
225 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700226 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700227 pw.print(" ["); pw.print(i); pw.print("] provider ");
228 pw.print(info.provider.flattenToShortString());
229 pw.println(':');
230 pw.print(" min=("); pw.print(info.minWidth);
231 pw.print("x"); pw.print(info.minHeight);
232 pw.print(") updatePeriodMillis=");
233 pw.print(info.updatePeriodMillis);
234 pw.print(" initialLayout=#");
235 pw.print(Integer.toHexString(info.initialLayout));
236 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 }
238
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700239 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700240 pw.println(" ");
241 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700243 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700244 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700245 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700246 pw.print(" hostId=");
247 pw.print(id.host.hostId); pw.print(' ');
248 pw.print(id.host.packageName); pw.print('/');
249 pw.println(id.host.uid);
250 if (id.provider != null) {
251 pw.print(" provider=");
252 pw.println(id.provider.info.provider.flattenToShortString());
253 }
254 if (id.host != null) {
255 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
256 }
257 if (id.views != null) {
258 pw.print(" views="); pw.println(id.views);
259 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 }
261
262 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700263 pw.println(" ");
264 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 for (int i=0; i<N; i++) {
266 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700267 pw.print(" ["); pw.print(i); pw.print("] hostId=");
268 pw.print(host.hostId); pw.print(' ');
269 pw.print(host.packageName); pw.print('/');
270 pw.print(host.uid); pw.println(':');
271 pw.print(" callbacks="); pw.println(host.callbacks);
272 pw.print(" instances.size="); pw.print(host.instances.size());
273 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 }
275 }
276 }
277
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700278 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700280 synchronized (mAppWidgetIds) {
281 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282
283 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
284
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700285 AppWidgetId id = new AppWidgetId();
286 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 id.host = host;
288
289 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700290 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291
292 saveStateLocked();
293
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700294 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 }
296 }
297
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700298 public void deleteAppWidgetId(int appWidgetId) {
299 synchronized (mAppWidgetIds) {
300 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700302 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 saveStateLocked();
304 }
305 }
306 }
307
308 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700309 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 int callingUid = getCallingUid();
311 Host host = lookupHostLocked(callingUid, hostId);
312 if (host != null) {
313 deleteHostLocked(host);
314 saveStateLocked();
315 }
316 }
317 }
318
319 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700320 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 int callingUid = getCallingUid();
322 final int N = mHosts.size();
323 boolean changed = false;
324 for (int i=N-1; i>=0; i--) {
325 Host host = mHosts.get(i);
326 if (host.uid == callingUid) {
327 deleteHostLocked(host);
328 changed = true;
329 }
330 }
331 if (changed) {
332 saveStateLocked();
333 }
334 }
335 }
336
337 void deleteHostLocked(Host host) {
338 final int N = host.instances.size();
339 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700340 AppWidgetId id = host.instances.get(i);
341 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 }
343 host.instances.clear();
344 mHosts.remove(host);
345 // it's gone or going away, abruptly drop the callback connection
346 host.callbacks = null;
347 }
348
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700349 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800350 // We first unbind all services that are bound to this id
351 unbindAppWidgetRemoteViewsServicesLocked(id);
352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 Host host = id.host;
354 host.instances.remove(id);
355 pruneHostLocked(host);
356
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700357 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358
359 Provider p = id.provider;
360 if (p != null) {
361 p.instances.remove(id);
362 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700363 // send the broacast saying that this appWidgetId has been deleted
364 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700366 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 mContext.sendBroadcast(intent);
368 if (p.instances.size() == 0) {
369 // cancel the future updates
370 cancelBroadcasts(p);
371
372 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700373 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 intent.setComponent(p.info.provider);
375 mContext.sendBroadcast(intent);
376 }
377 }
378 }
379 }
380
381 void cancelBroadcasts(Provider p) {
382 if (p.broadcast != null) {
383 mAlarmManager.cancel(p.broadcast);
384 long token = Binder.clearCallingIdentity();
385 try {
386 p.broadcast.cancel();
387 } finally {
388 Binder.restoreCallingIdentity(token);
389 }
390 p.broadcast = null;
391 }
392 }
393
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700394 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
395 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
396 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
397 synchronized (mAppWidgetIds) {
398 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700400 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 }
402 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700403 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 + id.provider.info.provider);
405 }
406 Provider p = lookupProviderLocked(provider);
407 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700408 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 }
410 if (p.zombie) {
411 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
412 + " safe mode: " + provider);
413 }
414
415 id.provider = p;
416 p.instances.add(id);
417 int instancesSize = p.instances.size();
418 if (instancesSize == 1) {
419 // tell the provider that it's ready
420 sendEnableIntentLocked(p);
421 }
422
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700423 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 // It's less critical when the next one happens, so when we schdule the next one,
425 // we add updatePeriodMillis to its start time. That time will have some slop,
426 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700427 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428
429 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700430 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 saveStateLocked();
432 }
433 }
434
Winson Chung84bbb022011-02-21 13:57:45 -0800435 // Binds to a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800436 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
437 synchronized (mAppWidgetIds) {
438 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
439 if (id == null) {
440 throw new IllegalArgumentException("bad appWidgetId");
441 }
442 final ComponentName componentName = intent.getComponent();
443 try {
444 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
445 PackageManager.GET_PERMISSIONS);
446 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
447 throw new SecurityException("Selected service does not require "
448 + android.Manifest.permission.BIND_REMOTEVIEWS
449 + ": " + componentName);
450 }
451 } catch (PackageManager.NameNotFoundException e) {
452 throw new IllegalArgumentException("Unknown component " + componentName);
453 }
454
Winson Chung16c8d8a2011-01-20 16:19:33 -0800455 // If there is already a connection made for this service intent, then disconnect from
456 // that first. (This does not allow multiple connections to the same service under
457 // the same key)
458 ServiceConnectionProxy conn = null;
Winson Chung84bbb022011-02-21 13:57:45 -0800459 FilterComparison fc = new FilterComparison(intent);
460 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800461 if (mBoundRemoteViewsServices.containsKey(key)) {
462 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
463 conn.disconnect();
464 mContext.unbindService(conn);
465 mBoundRemoteViewsServices.remove(key);
466 }
467
468 // Bind to the RemoteViewsService (which will trigger a callback to the
469 // RemoteViewsAdapter.onServiceConnected())
Winson Chung81f39eb2011-01-11 18:05:01 -0800470 final long token = Binder.clearCallingIdentity();
471 try {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800472 conn = new ServiceConnectionProxy(key, connection);
Winson Chung81f39eb2011-01-11 18:05:01 -0800473 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
474 mBoundRemoteViewsServices.put(key, conn);
475 } finally {
476 Binder.restoreCallingIdentity(token);
477 }
Winson Chung84bbb022011-02-21 13:57:45 -0800478
479 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
480 // when we can call back to the RemoteViewsService later to destroy associated
481 // factories.
Winson Chung22bc69d2011-02-25 15:13:38 -0800482 incrementAppWidgetServiceRefCount(appWidgetId, fc);
Winson Chung81f39eb2011-01-11 18:05:01 -0800483 }
484 }
485
Winson Chung84bbb022011-02-21 13:57:45 -0800486 // Unbinds from a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800487 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
488 synchronized (mAppWidgetIds) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800489 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
490 // RemoteViewsAdapter)
491 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
492 new FilterComparison(intent));
493 if (mBoundRemoteViewsServices.containsKey(key)) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800494 // We don't need to use the appWidgetId until after we are sure there is something
495 // to unbind. Note that this may mask certain issues with apps calling unbind()
496 // more than necessary.
497 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
498 if (id == null) {
499 throw new IllegalArgumentException("bad appWidgetId");
500 }
501
502 ServiceConnectionProxy conn =
503 (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
504 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800505 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800506 mBoundRemoteViewsServices.remove(key);
507 } else {
508 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
Winson Chung81f39eb2011-01-11 18:05:01 -0800509 }
510 }
511 }
512
Winson Chung84bbb022011-02-21 13:57:45 -0800513 // Unbinds from a RemoteViewsService when we delete an app widget
Winson Chung81f39eb2011-01-11 18:05:01 -0800514 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800515 int appWidgetId = id.appWidgetId;
516 // Unbind all connections to Services bound to this AppWidgetId
Winson Chung81f39eb2011-01-11 18:05:01 -0800517 Iterator<Pair<Integer, Intent.FilterComparison>> it =
518 mBoundRemoteViewsServices.keySet().iterator();
Winson Chung81f39eb2011-01-11 18:05:01 -0800519 while (it.hasNext()) {
520 final Pair<Integer, Intent.FilterComparison> key = it.next();
521 if (key.first.intValue() == appWidgetId) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800522 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
523 mBoundRemoteViewsServices.get(key);
524 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800525 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800526 it.remove();
Winson Chung81f39eb2011-01-11 18:05:01 -0800527 }
528 }
Winson Chung84bbb022011-02-21 13:57:45 -0800529
530 // Check if we need to destroy any services (if no other app widgets are
531 // referencing the same service)
532 decrementAppWidgetServiceRefCount(appWidgetId);
533 }
534
535 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
536 private void destroyRemoteViewsService(final Intent intent) {
537 final ServiceConnection conn = new ServiceConnection() {
538 @Override
539 public void onServiceConnected(ComponentName name, IBinder service) {
540 final IRemoteViewsFactory cb =
541 IRemoteViewsFactory.Stub.asInterface(service);
542 try {
543 cb.onDestroy(intent);
544 } catch (RemoteException e) {
545 e.printStackTrace();
546 }
547 mContext.unbindService(this);
548 }
549 @Override
550 public void onServiceDisconnected(android.content.ComponentName name) {
551 // Do nothing
552 }
553 };
554
555 // Bind to the service and remove the static intent->factory mapping in the
556 // RemoteViewsService.
557 final long token = Binder.clearCallingIdentity();
558 try {
559 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
560 } finally {
561 Binder.restoreCallingIdentity(token);
562 }
563 }
564
565 // Adds to the ref-count for a given RemoteViewsService intent
566 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
567 HashSet<Integer> appWidgetIds = null;
568 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
569 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
570 } else {
571 appWidgetIds = new HashSet<Integer>();
572 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
573 }
574 appWidgetIds.add(appWidgetId);
575 }
576
577 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
578 // the ref-count reaches zero.
579 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
580 Iterator<FilterComparison> it =
581 mRemoteViewsServicesAppWidgets.keySet().iterator();
582 while (it.hasNext()) {
583 final FilterComparison key = it.next();
584 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
585 if (ids.remove(appWidgetId)) {
586 // If we have removed the last app widget referencing this service, then we
587 // should destroy it and remove it from this set
588 if (ids.isEmpty()) {
589 destroyRemoteViewsService(key.getIntent());
590 it.remove();
591 }
592 }
593 }
Winson Chung81f39eb2011-01-11 18:05:01 -0800594 }
595
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700596 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
597 synchronized (mAppWidgetIds) {
598 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 if (id != null && id.provider != null && !id.provider.zombie) {
600 return id.provider.info;
601 }
602 return null;
603 }
604 }
605
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700606 public RemoteViews getAppWidgetViews(int appWidgetId) {
607 synchronized (mAppWidgetIds) {
608 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 if (id != null) {
610 return id.views;
611 }
612 return null;
613 }
614 }
615
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700616 public List<AppWidgetProviderInfo> getInstalledProviders() {
617 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700619 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 for (int i=0; i<N; i++) {
621 Provider p = mInstalledProviders.get(i);
622 if (!p.zombie) {
623 result.add(p.info);
624 }
625 }
626 return result;
627 }
628 }
629
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700630 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
631 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 return;
633 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700634 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 return;
636 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700637 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700639 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700641 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
642 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 }
644 }
645 }
646
Adam Cohen2dd21972010-08-15 18:20:04 -0700647 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
648 if (appWidgetIds == null) {
649 return;
650 }
651 if (appWidgetIds.length == 0) {
652 return;
653 }
654 final int N = appWidgetIds.length;
655
656 synchronized (mAppWidgetIds) {
657 for (int i=0; i<N; i++) {
658 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
659 updateAppWidgetInstanceLocked(id, views, true);
660 }
661 }
662 }
663
Winson Chung6394c0e2010-08-16 10:14:56 -0700664 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700665 if (appWidgetIds == null) {
666 return;
667 }
668 if (appWidgetIds.length == 0) {
669 return;
670 }
671 final int N = appWidgetIds.length;
672
673 synchronized (mAppWidgetIds) {
674 for (int i=0; i<N; i++) {
675 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700676 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700677 }
678 }
679 }
680
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700681 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
682 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 Provider p = lookupProviderLocked(provider);
684 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800685 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 return;
687 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700688 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 final int N = instances.size();
690 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700691 AppWidgetId id = instances.get(i);
692 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 }
694 }
695 }
696
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700697 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700698 updateAppWidgetInstanceLocked(id, views, false);
699 }
700
701 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700702 // allow for stale appWidgetIds and other badness
703 // lookup also checks that the calling process can access the appWidgetId
704 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700706
707 // We do not want to save this RemoteViews
708 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709
710 // is anyone listening?
711 if (id.host.callbacks != null) {
712 try {
713 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700714 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 } catch (RemoteException e) {
716 // It failed; remove the callback. No need to prune because
717 // we know that this host is still referenced by this instance.
718 id.host.callbacks = null;
719 }
720 }
721 }
722 }
723
Winson Chung6394c0e2010-08-16 10:14:56 -0700724 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700725 // allow for stale appWidgetIds and other badness
726 // lookup also checks that the calling process can access the appWidgetId
727 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
728 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700729 // is anyone listening?
730 if (id.host.callbacks != null) {
731 try {
732 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700733 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700734 } catch (RemoteException e) {
735 // It failed; remove the callback. No need to prune because
736 // we know that this host is still referenced by this instance.
737 id.host.callbacks = null;
738 }
739 }
740 }
741 }
742
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700743 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 List<RemoteViews> updatedViews) {
745 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700746 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
748 host.callbacks = callbacks;
749
750 updatedViews.clear();
751
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700752 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 int N = instances.size();
754 int[] updatedIds = new int[N];
755 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700756 AppWidgetId id = instances.get(i);
757 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 updatedViews.add(id.views);
759 }
760 return updatedIds;
761 }
762 }
763
764 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700765 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700767 if (host != null) {
768 host.callbacks = null;
769 pruneHostLocked(host);
770 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
772 }
773
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700774 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700776 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 return true;
778 }
779 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700780 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 return true;
782 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700783 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700785 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 return true;
787 }
788 // Nobody else can access it.
789 return false;
790 }
791
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700792 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700794 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700796 AppWidgetId id = mAppWidgetIds.get(i);
797 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 return id;
799 }
800 }
801 return null;
802 }
803
804 Provider lookupProviderLocked(ComponentName provider) {
Romain Guyd2671e12010-03-11 18:06:42 -0800805 final String className = provider.getClassName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 final int N = mInstalledProviders.size();
807 for (int i=0; i<N; i++) {
808 Provider p = mInstalledProviders.get(i);
Romain Guyd2671e12010-03-11 18:06:42 -0800809 if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 return p;
811 }
812 }
813 return null;
814 }
815
816 Host lookupHostLocked(int uid, int hostId) {
817 final int N = mHosts.size();
818 for (int i=0; i<N; i++) {
819 Host h = mHosts.get(i);
820 if (h.uid == uid && h.hostId == hostId) {
821 return h;
822 }
823 }
824 return null;
825 }
826
827 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
828 final int N = mHosts.size();
829 for (int i=0; i<N; i++) {
830 Host h = mHosts.get(i);
831 if (h.hostId == hostId && h.packageName.equals(packageName)) {
832 return h;
833 }
834 }
835 Host host = new Host();
836 host.packageName = packageName;
837 host.uid = uid;
838 host.hostId = hostId;
839 mHosts.add(host);
840 return host;
841 }
842
843 void pruneHostLocked(Host host) {
844 if (host.instances.size() == 0 && host.callbacks == null) {
845 mHosts.remove(host);
846 }
847 }
848
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700849 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 PackageManager pm = mPackageManager;
851
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700852 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
854 PackageManager.GET_META_DATA);
855
Bjorn Bringert5f857802010-02-10 23:09:48 +0000856 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 for (int i=0; i<N; i++) {
858 ResolveInfo ri = broadcastReceivers.get(i);
859 addProviderLocked(ri);
860 }
861 }
862
863 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800864 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
865 return false;
866 }
867 if (!ri.activityInfo.isEnabled()) {
868 return false;
869 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
871 ri.activityInfo.name), ri);
872 if (p != null) {
873 mInstalledProviders.add(p);
874 return true;
875 } else {
876 return false;
877 }
878 }
879
880 void removeProviderLocked(int index, Provider p) {
881 int N = p.instances.size();
882 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700883 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700885 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 // Stop telling the host about updates for this from now on
887 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700888 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700890 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 id.provider = null;
892 pruneHostLocked(id.host);
893 id.host = null;
894 }
895 p.instances.clear();
896 mInstalledProviders.remove(index);
897 // no need to send the DISABLE broadcast, since the receiver is gone anyway
898 cancelBroadcasts(p);
899 }
900
901 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700902 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 intent.setComponent(p.info.provider);
904 mContext.sendBroadcast(intent);
905 }
906
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700907 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
908 if (appWidgetIds != null && appWidgetIds.length > 0) {
909 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
910 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 intent.setComponent(p.info.provider);
912 mContext.sendBroadcast(intent);
913 }
914 }
915
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700916 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 if (p.info.updatePeriodMillis > 0) {
918 // if this is the first instance, set the alarm. otherwise,
919 // rely on the fact that we've already set it and that
920 // PendingIntent.getBroadcast will update the extras.
921 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700922 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
923 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 intent.setComponent(p.info.provider);
925 long token = Binder.clearCallingIdentity();
926 try {
927 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
928 PendingIntent.FLAG_UPDATE_CURRENT);
929 } finally {
930 Binder.restoreCallingIdentity(token);
931 }
932 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700933 long period = p.info.updatePeriodMillis;
934 if (period < MIN_UPDATE_PERIOD) {
935 period = MIN_UPDATE_PERIOD;
936 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700938 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 }
940 }
941 }
942
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700943 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700945 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700947 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700949 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 }
951
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700952 public int[] getAppWidgetIds(ComponentName provider) {
953 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 Provider p = lookupProviderLocked(provider);
955 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700956 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 } else {
958 return new int[0];
959 }
960 }
961 }
962
963 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
964 Provider p = null;
965
966 ActivityInfo activityInfo = ri.activityInfo;
967 XmlResourceParser parser = null;
968 try {
969 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700970 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800972 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700973 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 return null;
975 }
976
977 AttributeSet attrs = Xml.asAttributeSet(parser);
978
979 int type;
980 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
981 && type != XmlPullParser.START_TAG) {
982 // drain whitespace, comments, etc.
983 }
984
985 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700986 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800987 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700988 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 return null;
990 }
991
992 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700993 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
Romain Guyd2671e12010-03-11 18:06:42 -0800994 // If metaData was null, we would have returned earlier when getting
995 // the parser No need to do the check here
996 info.oldName = activityInfo.metaData.getString(
997 AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998
999 info.provider = component;
1000 p.uid = activityInfo.applicationInfo.uid;
1001
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001002 Resources res = mPackageManager.getResourcesForApplication(
1003 activityInfo.applicationInfo);
1004
1005 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001006 com.android.internal.R.styleable.AppWidgetProviderInfo);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001007
1008 // These dimensions has to be resolved in the application's context.
1009 // We simply send back the raw complex data, which will be
1010 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1011 TypedValue value = sa.peekValue(
1012 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1013 info.minWidth = value != null ? value.data : 0;
1014 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1015 info.minHeight = value != null ? value.data : 0;
1016
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001018 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001020 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001022 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 if (className != null) {
1024 info.configure = new ComponentName(component.getPackageName(), className);
1025 }
1026 info.label = activityInfo.loadLabel(mPackageManager).toString();
1027 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001028 info.previewImage = sa.getResourceId(
1029 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -07001030 info.autoAdvanceViewId = sa.getResourceId(
1031 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001032
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 sa.recycle();
1034 } catch (Exception e) {
1035 // Ok to catch Exception here, because anything going wrong because
1036 // of what a client process passes to us should not be fatal for the
1037 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001038 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 return null;
1040 } finally {
1041 if (parser != null) parser.close();
1042 }
1043 return p;
1044 }
1045
1046 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1047 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1048 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1049 throw new PackageManager.NameNotFoundException();
1050 }
1051 return pkgInfo.applicationInfo.uid;
1052 }
1053
1054 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1055 int callingUid = getCallingUid();
1056 int packageUid;
1057 try {
1058 packageUid = getUidForPackage(packageName);
1059 } catch (PackageManager.NameNotFoundException ex) {
1060 throw new IllegalArgumentException("packageName and uid don't match packageName="
1061 + packageName);
1062 }
Marco Nelissen54796e72009-04-30 15:16:30 -07001063 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 throw new IllegalArgumentException("packageName and uid don't match packageName="
1065 + packageName);
1066 }
1067 return callingUid;
1068 }
1069
1070 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001071 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 final int N = mInstalledProviders.size();
1073 for (int i=0; i<N; i++) {
1074 Provider p = mInstalledProviders.get(i);
1075 if (p.instances.size() > 0) {
1076 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001077 int[] appWidgetIds = getAppWidgetIds(p);
1078 sendUpdateIntentLocked(p, appWidgetIds);
1079 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 }
1081 }
1082 }
1083 }
1084
1085 // only call from initialization -- it assumes that the data structures are all empty
1086 void loadStateLocked() {
1087 File temp = savedStateTempFile();
1088 File real = savedStateRealFile();
1089
1090 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1091 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1092 // fully written and delete it.
1093 if (real.exists()) {
1094 readStateFromFileLocked(real);
1095 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001096 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 temp.delete();
1098 }
1099 } else if (temp.exists()) {
1100 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001101 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 temp.renameTo(real);
1103 }
1104 }
1105
1106 void saveStateLocked() {
1107 File temp = savedStateTempFile();
1108 File real = savedStateRealFile();
1109
1110 if (!real.exists()) {
1111 // If the real one doesn't exist, it's either because this is the first time
1112 // or because something went wrong while copying them. In this case, we can't
1113 // trust anything that's in temp. In order to have the loadState code not
1114 // use the temporary one until it's fully written, create an empty file
1115 // for real, which will we'll shortly delete.
1116 try {
Romain Guya5475592009-07-01 17:20:08 -07001117 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 real.createNewFile();
1119 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001120 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 }
1122 }
1123
1124 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001125 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 temp.delete();
1127 }
1128
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001129 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001130 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001131 return;
1132 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133
Romain Guya5475592009-07-01 17:20:08 -07001134 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001136 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 temp.renameTo(real);
1138 }
1139
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001140 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 FileOutputStream stream = null;
1142 int N;
1143
1144 try {
1145 stream = new FileOutputStream(file, false);
1146 XmlSerializer out = new FastXmlSerializer();
1147 out.setOutput(stream, "utf-8");
1148 out.startDocument(null, true);
1149
1150
1151 out.startTag(null, "gs");
1152
1153 int providerIndex = 0;
1154 N = mInstalledProviders.size();
1155 for (int i=0; i<N; i++) {
1156 Provider p = mInstalledProviders.get(i);
1157 if (p.instances.size() > 0) {
1158 out.startTag(null, "p");
1159 out.attribute(null, "pkg", p.info.provider.getPackageName());
1160 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001161 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 p.tag = providerIndex;
1163 providerIndex++;
1164 }
1165 }
1166
1167 N = mHosts.size();
1168 for (int i=0; i<N; i++) {
1169 Host host = mHosts.get(i);
1170 out.startTag(null, "h");
1171 out.attribute(null, "pkg", host.packageName);
1172 out.attribute(null, "id", Integer.toHexString(host.hostId));
1173 out.endTag(null, "h");
1174 host.tag = i;
1175 }
1176
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001177 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001179 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001181 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1183 if (id.provider != null) {
1184 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1185 }
1186 out.endTag(null, "g");
1187 }
1188
1189 out.endTag(null, "gs");
1190
1191 out.endDocument();
1192 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001193 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 } catch (IOException e) {
1195 try {
1196 if (stream != null) {
1197 stream.close();
1198 }
1199 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001200 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 }
1202 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001203 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 file.delete();
1205 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001206 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 }
1208 }
1209
1210 void readStateFromFileLocked(File file) {
1211 FileInputStream stream = null;
1212
1213 boolean success = false;
1214
1215 try {
1216 stream = new FileInputStream(file);
1217 XmlPullParser parser = Xml.newPullParser();
1218 parser.setInput(stream, null);
1219
1220 int type;
1221 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001222 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001223 do {
1224 type = parser.next();
1225 if (type == XmlPullParser.START_TAG) {
1226 String tag = parser.getName();
1227 if ("p".equals(tag)) {
1228 // TODO: do we need to check that this package has the same signature
1229 // as before?
1230 String pkg = parser.getAttributeValue(null, "pkg");
1231 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001232
1233 final PackageManager packageManager = mContext.getPackageManager();
1234 try {
1235 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1236 } catch (PackageManager.NameNotFoundException e) {
1237 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1238 new String[] { pkg });
1239 pkg = pkgs[0];
1240 }
1241
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1243 if (p == null && mSafeMode) {
1244 // if we're in safe mode, make a temporary one
1245 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001246 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 p.info.provider = new ComponentName(pkg, cl);
1248 p.zombie = true;
1249 mInstalledProviders.add(p);
1250 }
1251 if (p != null) {
1252 // if it wasn't uninstalled or something
1253 loadedProviders.put(providerIndex, p);
1254 }
1255 providerIndex++;
1256 }
1257 else if ("h".equals(tag)) {
1258 Host host = new Host();
1259
1260 // TODO: do we need to check that this package has the same signature
1261 // as before?
1262 host.packageName = parser.getAttributeValue(null, "pkg");
1263 try {
1264 host.uid = getUidForPackage(host.packageName);
1265 } catch (PackageManager.NameNotFoundException ex) {
1266 host.zombie = true;
1267 }
1268 if (!host.zombie || mSafeMode) {
1269 // In safe mode, we don't discard the hosts we don't recognize
1270 // so that they're not pruned from our list. Otherwise, we do.
1271 host.hostId = Integer.parseInt(
1272 parser.getAttributeValue(null, "id"), 16);
1273 mHosts.add(host);
1274 }
1275 }
1276 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001277 AppWidgetId id = new AppWidgetId();
1278 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1279 if (id.appWidgetId >= mNextAppWidgetId) {
1280 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281 }
1282
1283 String providerString = parser.getAttributeValue(null, "p");
1284 if (providerString != null) {
1285 // there's no provider if it hasn't been bound yet.
1286 // maybe we don't have to save this, but it brings the system
1287 // to the state it was in.
1288 int pIndex = Integer.parseInt(providerString, 16);
1289 id.provider = loadedProviders.get(pIndex);
1290 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001291 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 + pIndex + " which is " + id.provider);
1293 }
1294 if (id.provider == null) {
1295 // This provider is gone. We just let the host figure out
1296 // that this happened when it fails to load it.
1297 continue;
1298 }
1299 }
1300
1301 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1302 id.host = mHosts.get(hIndex);
1303 if (id.host == null) {
1304 // This host is gone.
1305 continue;
1306 }
1307
1308 if (id.provider != null) {
1309 id.provider.instances.add(id);
1310 }
1311 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001312 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001313 }
1314 }
1315 } while (type != XmlPullParser.END_DOCUMENT);
1316 success = true;
1317 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001318 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001319 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001320 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001322 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001324 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001326 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 }
1328 try {
1329 if (stream != null) {
1330 stream.close();
1331 }
1332 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001333 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 }
1335
1336 if (success) {
1337 // delete any hosts that didn't manage to get connected (should happen)
1338 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001339 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 pruneHostLocked(mHosts.get(i));
1341 }
1342 } else {
1343 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001344 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 mHosts.clear();
1346 final int N = mInstalledProviders.size();
1347 for (int i=0; i<N; i++) {
1348 mInstalledProviders.get(i).instances.clear();
1349 }
1350 }
1351 }
1352
1353 File savedStateTempFile() {
1354 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1355 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1356 }
1357
1358 File savedStateRealFile() {
1359 return new File("/data/system/" + SETTINGS_FILENAME);
1360 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1361 }
1362
1363 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1364 public void onReceive(Context context, Intent intent) {
1365 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001366 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1368 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001369 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1370 Locale revised = Locale.getDefault();
1371 if (revised == null || mLocale == null ||
1372 !(revised.equals(mLocale))) {
1373 mLocale = revised;
1374
1375 synchronized (mAppWidgetIds) {
1376 int N = mInstalledProviders.size();
1377 for (int i=N-1; i>=0; i--) {
1378 Provider p = mInstalledProviders.get(i);
1379 String pkgName = p.info.provider.getPackageName();
1380 updateProvidersForPackageLocked(pkgName);
1381 }
1382 saveStateLocked();
1383 }
1384 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001386 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001387 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001388 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001389 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001390 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1391 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001392 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001393 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1394 added = false;
1395 } else {
1396 Uri uri = intent.getData();
1397 if (uri == null) {
1398 return;
1399 }
1400 String pkgName = uri.getSchemeSpecificPart();
1401 if (pkgName == null) {
1402 return;
1403 }
1404 pkgList = new String[] { pkgName };
1405 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001406 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001407 }
1408 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409 return;
1410 }
Joe Onoratod070e892011-01-07 20:50:37 -08001411 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001412 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001414 if (changed || (extras != null &&
1415 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001416 for (String pkgName : pkgList) {
1417 // The package was just upgraded
1418 updateProvidersForPackageLocked(pkgName);
1419 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001420 } else {
1421 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001422 for (String pkgName : pkgList) {
1423 addProvidersForPackageLocked(pkgName);
1424 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001425 }
1426 saveStateLocked();
1427 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001428 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 Bundle extras = intent.getExtras();
1430 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1431 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1432 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001433 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001434 for (String pkgName : pkgList) {
1435 removeProvidersForPackageLocked(pkgName);
1436 saveStateLocked();
1437 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 }
1439 }
1440 }
1441 }
1442 }
1443 };
1444
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001445 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001446 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001447 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001448 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1449 PackageManager.GET_META_DATA);
1450
Bjorn Bringert5f857802010-02-10 23:09:48 +00001451 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001452 for (int i=0; i<N; i++) {
1453 ResolveInfo ri = broadcastReceivers.get(i);
1454 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001455 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1456 continue;
1457 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001458 if (pkgName.equals(ai.packageName)) {
1459 addProviderLocked(ri);
1460 }
1461 }
1462 }
1463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001464 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001465 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001466 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001467 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1469 PackageManager.GET_META_DATA);
1470
1471 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001472 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001473 for (int i=0; i<N; i++) {
1474 ResolveInfo ri = broadcastReceivers.get(i);
1475 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001476 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1477 continue;
1478 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 if (pkgName.equals(ai.packageName)) {
1480 ComponentName component = new ComponentName(ai.packageName, ai.name);
1481 Provider p = lookupProviderLocked(component);
1482 if (p == null) {
1483 if (addProviderLocked(ri)) {
1484 keep.add(ai.name);
1485 }
1486 } else {
1487 Provider parsed = parseProviderInfoXml(component, ri);
1488 if (parsed != null) {
1489 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001490 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 p.info = parsed.info;
1492 // If it's enabled
1493 final int M = p.instances.size();
1494 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001495 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 // Reschedule for the new updatePeriodMillis (don't worry about handling
1497 // it specially if updatePeriodMillis didn't change because we just sent
1498 // an update, and the next one will be updatePeriodMillis from now).
1499 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001500 registerForBroadcastsLocked(p, appWidgetIds);
1501 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001503 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001504 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505 if (id.host != null && id.host.callbacks != null) {
1506 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001507 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 } catch (RemoteException ex) {
1509 // It failed; remove the callback. No need to prune because
1510 // we know that this host is still referenced by this
1511 // instance.
1512 id.host.callbacks = null;
1513 }
1514 }
1515 }
1516 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001517 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 }
1519 }
1520 }
1521 }
1522 }
1523
1524 // prune the ones we don't want to keep
1525 N = mInstalledProviders.size();
1526 for (int i=N-1; i>=0; i--) {
1527 Provider p = mInstalledProviders.get(i);
1528 if (pkgName.equals(p.info.provider.getPackageName())
1529 && !keep.contains(p.info.provider.getClassName())) {
1530 removeProviderLocked(i, p);
1531 }
1532 }
1533 }
1534
1535 void removeProvidersForPackageLocked(String pkgName) {
1536 int N = mInstalledProviders.size();
1537 for (int i=N-1; i>=0; i--) {
1538 Provider p = mInstalledProviders.get(i);
1539 if (pkgName.equals(p.info.provider.getPackageName())) {
1540 removeProviderLocked(i, p);
1541 }
1542 }
1543
1544 // Delete the hosts for this package too
1545 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001546 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001547 // so we don't need to worry about sending DISABLE broadcasts to them.
1548 N = mHosts.size();
1549 for (int i=N-1; i>=0; i--) {
1550 Host host = mHosts.get(i);
1551 if (pkgName.equals(host.packageName)) {
1552 deleteHostLocked(host);
1553 }
1554 }
1555 }
1556}
1557