blob: a4a95a0ace90082e1025fcbb6a6d4bf5ac831b62 [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 Onorato331fbdc2010-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);
Adam Cohenc2be22c2011-03-16 16:33:53 -0700138 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800139 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();
Adam Cohenc2be22c2011-03-16 16:33:53 -0700150 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800151 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);
Adam Cohenc2be22c2011-03-16 16:33:53 -0700544 } catch (Exception e) {
Winson Chung84bbb022011-02-21 13:57:45 -0800545 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 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800976
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -0800978
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 int type;
980 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
981 && type != XmlPullParser.START_TAG) {
982 // drain whitespace, comments, etc.
983 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800984
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 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);
Adam Cohend2e20de2011-02-25 12:03:37 -08001004
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001005 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001006 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001007
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001008 // 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;
Adam Cohend2e20de2011-02-25 12:03:37 -08001016
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);
Adam Cohen9611f2e2011-02-28 13:39:38 -08001032 info.resizeMode = sa.getInt(
Adam Cohend2e20de2011-02-25 12:03:37 -08001033 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1034 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001035
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 sa.recycle();
1037 } catch (Exception e) {
1038 // Ok to catch Exception here, because anything going wrong because
1039 // of what a client process passes to us should not be fatal for the
1040 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001041 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 return null;
1043 } finally {
1044 if (parser != null) parser.close();
1045 }
1046 return p;
1047 }
1048
1049 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1050 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1051 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1052 throw new PackageManager.NameNotFoundException();
1053 }
1054 return pkgInfo.applicationInfo.uid;
1055 }
1056
1057 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1058 int callingUid = getCallingUid();
1059 int packageUid;
1060 try {
1061 packageUid = getUidForPackage(packageName);
1062 } catch (PackageManager.NameNotFoundException ex) {
1063 throw new IllegalArgumentException("packageName and uid don't match packageName="
1064 + packageName);
1065 }
Marco Nelissen54796e72009-04-30 15:16:30 -07001066 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 throw new IllegalArgumentException("packageName and uid don't match packageName="
1068 + packageName);
1069 }
1070 return callingUid;
1071 }
1072
1073 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001074 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 final int N = mInstalledProviders.size();
1076 for (int i=0; i<N; i++) {
1077 Provider p = mInstalledProviders.get(i);
1078 if (p.instances.size() > 0) {
1079 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001080 int[] appWidgetIds = getAppWidgetIds(p);
1081 sendUpdateIntentLocked(p, appWidgetIds);
1082 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 }
1084 }
1085 }
1086 }
1087
1088 // only call from initialization -- it assumes that the data structures are all empty
1089 void loadStateLocked() {
1090 File temp = savedStateTempFile();
1091 File real = savedStateRealFile();
1092
1093 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1094 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1095 // fully written and delete it.
1096 if (real.exists()) {
1097 readStateFromFileLocked(real);
1098 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001099 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 temp.delete();
1101 }
1102 } else if (temp.exists()) {
1103 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001104 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 temp.renameTo(real);
1106 }
1107 }
1108
1109 void saveStateLocked() {
1110 File temp = savedStateTempFile();
1111 File real = savedStateRealFile();
1112
1113 if (!real.exists()) {
1114 // If the real one doesn't exist, it's either because this is the first time
1115 // or because something went wrong while copying them. In this case, we can't
1116 // trust anything that's in temp. In order to have the loadState code not
1117 // use the temporary one until it's fully written, create an empty file
1118 // for real, which will we'll shortly delete.
1119 try {
Romain Guya5475592009-07-01 17:20:08 -07001120 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 real.createNewFile();
1122 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001123 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 }
1125 }
1126
1127 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001128 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129 temp.delete();
1130 }
1131
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001132 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001133 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001134 return;
1135 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136
Romain Guya5475592009-07-01 17:20:08 -07001137 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001139 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 temp.renameTo(real);
1141 }
1142
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001143 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 FileOutputStream stream = null;
1145 int N;
1146
1147 try {
1148 stream = new FileOutputStream(file, false);
1149 XmlSerializer out = new FastXmlSerializer();
1150 out.setOutput(stream, "utf-8");
1151 out.startDocument(null, true);
1152
1153
1154 out.startTag(null, "gs");
1155
1156 int providerIndex = 0;
1157 N = mInstalledProviders.size();
1158 for (int i=0; i<N; i++) {
1159 Provider p = mInstalledProviders.get(i);
1160 if (p.instances.size() > 0) {
1161 out.startTag(null, "p");
1162 out.attribute(null, "pkg", p.info.provider.getPackageName());
1163 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001164 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001165 p.tag = providerIndex;
1166 providerIndex++;
1167 }
1168 }
1169
1170 N = mHosts.size();
1171 for (int i=0; i<N; i++) {
1172 Host host = mHosts.get(i);
1173 out.startTag(null, "h");
1174 out.attribute(null, "pkg", host.packageName);
1175 out.attribute(null, "id", Integer.toHexString(host.hostId));
1176 out.endTag(null, "h");
1177 host.tag = i;
1178 }
1179
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001180 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001182 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001184 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1186 if (id.provider != null) {
1187 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1188 }
1189 out.endTag(null, "g");
1190 }
1191
1192 out.endTag(null, "gs");
1193
1194 out.endDocument();
1195 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001196 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 } catch (IOException e) {
1198 try {
1199 if (stream != null) {
1200 stream.close();
1201 }
1202 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001203 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 }
1205 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001206 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 file.delete();
1208 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001209 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210 }
1211 }
1212
1213 void readStateFromFileLocked(File file) {
1214 FileInputStream stream = null;
1215
1216 boolean success = false;
1217
1218 try {
1219 stream = new FileInputStream(file);
1220 XmlPullParser parser = Xml.newPullParser();
1221 parser.setInput(stream, null);
1222
1223 int type;
1224 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001225 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 do {
1227 type = parser.next();
1228 if (type == XmlPullParser.START_TAG) {
1229 String tag = parser.getName();
1230 if ("p".equals(tag)) {
1231 // TODO: do we need to check that this package has the same signature
1232 // as before?
1233 String pkg = parser.getAttributeValue(null, "pkg");
1234 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c2010-03-11 15:30:02 -08001235
1236 final PackageManager packageManager = mContext.getPackageManager();
1237 try {
1238 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1239 } catch (PackageManager.NameNotFoundException e) {
1240 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1241 new String[] { pkg });
1242 pkg = pkgs[0];
1243 }
1244
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1246 if (p == null && mSafeMode) {
1247 // if we're in safe mode, make a temporary one
1248 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001249 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 p.info.provider = new ComponentName(pkg, cl);
1251 p.zombie = true;
1252 mInstalledProviders.add(p);
1253 }
1254 if (p != null) {
1255 // if it wasn't uninstalled or something
1256 loadedProviders.put(providerIndex, p);
1257 }
1258 providerIndex++;
1259 }
1260 else if ("h".equals(tag)) {
1261 Host host = new Host();
1262
1263 // TODO: do we need to check that this package has the same signature
1264 // as before?
1265 host.packageName = parser.getAttributeValue(null, "pkg");
1266 try {
1267 host.uid = getUidForPackage(host.packageName);
1268 } catch (PackageManager.NameNotFoundException ex) {
1269 host.zombie = true;
1270 }
1271 if (!host.zombie || mSafeMode) {
1272 // In safe mode, we don't discard the hosts we don't recognize
1273 // so that they're not pruned from our list. Otherwise, we do.
1274 host.hostId = Integer.parseInt(
1275 parser.getAttributeValue(null, "id"), 16);
1276 mHosts.add(host);
1277 }
1278 }
1279 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001280 AppWidgetId id = new AppWidgetId();
1281 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1282 if (id.appWidgetId >= mNextAppWidgetId) {
1283 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001284 }
1285
1286 String providerString = parser.getAttributeValue(null, "p");
1287 if (providerString != null) {
1288 // there's no provider if it hasn't been bound yet.
1289 // maybe we don't have to save this, but it brings the system
1290 // to the state it was in.
1291 int pIndex = Integer.parseInt(providerString, 16);
1292 id.provider = loadedProviders.get(pIndex);
1293 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001294 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 + pIndex + " which is " + id.provider);
1296 }
1297 if (id.provider == null) {
1298 // This provider is gone. We just let the host figure out
1299 // that this happened when it fails to load it.
1300 continue;
1301 }
1302 }
1303
1304 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1305 id.host = mHosts.get(hIndex);
1306 if (id.host == null) {
1307 // This host is gone.
1308 continue;
1309 }
1310
1311 if (id.provider != null) {
1312 id.provider.instances.add(id);
1313 }
1314 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001315 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001316 }
1317 }
1318 } while (type != XmlPullParser.END_DOCUMENT);
1319 success = true;
1320 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001321 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001323 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001325 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001326 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001327 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001328 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001329 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 }
1331 try {
1332 if (stream != null) {
1333 stream.close();
1334 }
1335 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001336 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 }
1338
1339 if (success) {
1340 // delete any hosts that didn't manage to get connected (should happen)
1341 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001342 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 pruneHostLocked(mHosts.get(i));
1344 }
1345 } else {
1346 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001347 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348 mHosts.clear();
1349 final int N = mInstalledProviders.size();
1350 for (int i=0; i<N; i++) {
1351 mInstalledProviders.get(i).instances.clear();
1352 }
1353 }
1354 }
1355
1356 File savedStateTempFile() {
1357 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1358 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1359 }
1360
1361 File savedStateRealFile() {
1362 return new File("/data/system/" + SETTINGS_FILENAME);
1363 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1364 }
1365
1366 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1367 public void onReceive(Context context, Intent intent) {
1368 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001369 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1371 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001372 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1373 Locale revised = Locale.getDefault();
1374 if (revised == null || mLocale == null ||
1375 !(revised.equals(mLocale))) {
1376 mLocale = revised;
1377
1378 synchronized (mAppWidgetIds) {
1379 int N = mInstalledProviders.size();
1380 for (int i=N-1; i>=0; i--) {
1381 Provider p = mInstalledProviders.get(i);
1382 String pkgName = p.info.provider.getPackageName();
1383 updateProvidersForPackageLocked(pkgName);
1384 }
1385 saveStateLocked();
1386 }
1387 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001389 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001390 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001391 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001392 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001393 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1394 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001395 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001396 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1397 added = false;
1398 } else {
1399 Uri uri = intent.getData();
1400 if (uri == null) {
1401 return;
1402 }
1403 String pkgName = uri.getSchemeSpecificPart();
1404 if (pkgName == null) {
1405 return;
1406 }
1407 pkgList = new String[] { pkgName };
1408 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001409 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001410 }
1411 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001412 return;
1413 }
Joe Onoratod070e892011-01-07 20:50:37 -08001414 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001415 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001417 if (changed || (extras != null &&
1418 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001419 for (String pkgName : pkgList) {
1420 // The package was just upgraded
1421 updateProvidersForPackageLocked(pkgName);
1422 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 } else {
1424 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001425 for (String pkgName : pkgList) {
1426 addProvidersForPackageLocked(pkgName);
1427 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 }
1429 saveStateLocked();
1430 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001431 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 Bundle extras = intent.getExtras();
1433 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1434 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1435 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001436 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001437 for (String pkgName : pkgList) {
1438 removeProvidersForPackageLocked(pkgName);
1439 saveStateLocked();
1440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001441 }
1442 }
1443 }
1444 }
1445 }
1446 };
1447
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001448 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001449 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001450 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1452 PackageManager.GET_META_DATA);
1453
Bjorn Bringert5f857802010-02-10 23:09:48 +00001454 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001455 for (int i=0; i<N; i++) {
1456 ResolveInfo ri = broadcastReceivers.get(i);
1457 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc2010-08-24 17:02:09 -04001458 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1459 continue;
1460 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 if (pkgName.equals(ai.packageName)) {
1462 addProviderLocked(ri);
1463 }
1464 }
1465 }
1466
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001467 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001468 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001469 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001470 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001471 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1472 PackageManager.GET_META_DATA);
1473
1474 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001475 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 for (int i=0; i<N; i++) {
1477 ResolveInfo ri = broadcastReceivers.get(i);
1478 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc2010-08-24 17:02:09 -04001479 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1480 continue;
1481 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 if (pkgName.equals(ai.packageName)) {
1483 ComponentName component = new ComponentName(ai.packageName, ai.name);
1484 Provider p = lookupProviderLocked(component);
1485 if (p == null) {
1486 if (addProviderLocked(ri)) {
1487 keep.add(ai.name);
1488 }
1489 } else {
1490 Provider parsed = parseProviderInfoXml(component, ri);
1491 if (parsed != null) {
1492 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001493 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001494 p.info = parsed.info;
1495 // If it's enabled
1496 final int M = p.instances.size();
1497 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001498 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 // Reschedule for the new updatePeriodMillis (don't worry about handling
1500 // it specially if updatePeriodMillis didn't change because we just sent
1501 // an update, and the next one will be updatePeriodMillis from now).
1502 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001503 registerForBroadcastsLocked(p, appWidgetIds);
1504 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001506 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001507 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 if (id.host != null && id.host.callbacks != null) {
1509 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001510 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 } catch (RemoteException ex) {
1512 // It failed; remove the callback. No need to prune because
1513 // we know that this host is still referenced by this
1514 // instance.
1515 id.host.callbacks = null;
1516 }
1517 }
1518 }
1519 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001520 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001521 }
1522 }
1523 }
1524 }
1525 }
1526
1527 // prune the ones we don't want to keep
1528 N = mInstalledProviders.size();
1529 for (int i=N-1; i>=0; i--) {
1530 Provider p = mInstalledProviders.get(i);
1531 if (pkgName.equals(p.info.provider.getPackageName())
1532 && !keep.contains(p.info.provider.getClassName())) {
1533 removeProviderLocked(i, p);
1534 }
1535 }
1536 }
1537
1538 void removeProvidersForPackageLocked(String pkgName) {
1539 int N = mInstalledProviders.size();
1540 for (int i=N-1; i>=0; i--) {
1541 Provider p = mInstalledProviders.get(i);
1542 if (pkgName.equals(p.info.provider.getPackageName())) {
1543 removeProviderLocked(i, p);
1544 }
1545 }
1546
1547 // Delete the hosts for this package too
1548 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001549 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 // so we don't need to worry about sending DISABLE broadcasts to them.
1551 N = mHosts.size();
1552 for (int i=N-1; i>=0; i--) {
1553 Host host = mHosts.get(i);
1554 if (pkgName.equals(host.packageName)) {
1555 deleteHostLocked(host);
1556 }
1557 }
1558 }
1559}
1560