blob: c03b994423aa898b9bab76a3ede3cbd2b081cef3 [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 Cohen2625fea2011-03-23 17:24:30 -0700544 } catch (RemoteException e) {
545 e.printStackTrace();
546 } catch (RuntimeException e) {
Winson Chung84bbb022011-02-21 13:57:45 -0800547 e.printStackTrace();
548 }
549 mContext.unbindService(this);
550 }
551 @Override
552 public void onServiceDisconnected(android.content.ComponentName name) {
553 // Do nothing
554 }
555 };
556
557 // Bind to the service and remove the static intent->factory mapping in the
558 // RemoteViewsService.
559 final long token = Binder.clearCallingIdentity();
560 try {
561 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
562 } finally {
563 Binder.restoreCallingIdentity(token);
564 }
565 }
566
567 // Adds to the ref-count for a given RemoteViewsService intent
568 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
569 HashSet<Integer> appWidgetIds = null;
570 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
571 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
572 } else {
573 appWidgetIds = new HashSet<Integer>();
574 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
575 }
576 appWidgetIds.add(appWidgetId);
577 }
578
579 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
580 // the ref-count reaches zero.
581 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
582 Iterator<FilterComparison> it =
583 mRemoteViewsServicesAppWidgets.keySet().iterator();
584 while (it.hasNext()) {
585 final FilterComparison key = it.next();
586 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
587 if (ids.remove(appWidgetId)) {
588 // If we have removed the last app widget referencing this service, then we
589 // should destroy it and remove it from this set
590 if (ids.isEmpty()) {
591 destroyRemoteViewsService(key.getIntent());
592 it.remove();
593 }
594 }
595 }
Winson Chung81f39eb2011-01-11 18:05:01 -0800596 }
597
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700598 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
599 synchronized (mAppWidgetIds) {
600 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 if (id != null && id.provider != null && !id.provider.zombie) {
602 return id.provider.info;
603 }
604 return null;
605 }
606 }
607
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700608 public RemoteViews getAppWidgetViews(int appWidgetId) {
609 synchronized (mAppWidgetIds) {
610 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 if (id != null) {
612 return id.views;
613 }
614 return null;
615 }
616 }
617
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700618 public List<AppWidgetProviderInfo> getInstalledProviders() {
619 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700621 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 for (int i=0; i<N; i++) {
623 Provider p = mInstalledProviders.get(i);
624 if (!p.zombie) {
625 result.add(p.info);
626 }
627 }
628 return result;
629 }
630 }
631
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700632 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
633 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 return;
635 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700636 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 return;
638 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700639 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700641 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700643 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
644 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 }
646 }
647 }
648
Adam Cohen2dd21972010-08-15 18:20:04 -0700649 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
650 if (appWidgetIds == null) {
651 return;
652 }
653 if (appWidgetIds.length == 0) {
654 return;
655 }
656 final int N = appWidgetIds.length;
657
658 synchronized (mAppWidgetIds) {
659 for (int i=0; i<N; i++) {
660 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
661 updateAppWidgetInstanceLocked(id, views, true);
662 }
663 }
664 }
665
Winson Chung6394c0e2010-08-16 10:14:56 -0700666 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700667 if (appWidgetIds == null) {
668 return;
669 }
670 if (appWidgetIds.length == 0) {
671 return;
672 }
673 final int N = appWidgetIds.length;
674
675 synchronized (mAppWidgetIds) {
676 for (int i=0; i<N; i++) {
677 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700678 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700679 }
680 }
681 }
682
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700683 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
684 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 Provider p = lookupProviderLocked(provider);
686 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800687 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 return;
689 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700690 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 final int N = instances.size();
692 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700693 AppWidgetId id = instances.get(i);
694 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 }
696 }
697 }
698
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700699 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700700 updateAppWidgetInstanceLocked(id, views, false);
701 }
702
703 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700704 // allow for stale appWidgetIds and other badness
705 // lookup also checks that the calling process can access the appWidgetId
706 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700708
709 // We do not want to save this RemoteViews
710 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711
712 // is anyone listening?
713 if (id.host.callbacks != null) {
714 try {
715 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700716 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 } catch (RemoteException e) {
718 // It failed; remove the callback. No need to prune because
719 // we know that this host is still referenced by this instance.
720 id.host.callbacks = null;
721 }
722 }
723 }
724 }
725
Winson Chung6394c0e2010-08-16 10:14:56 -0700726 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700727 // allow for stale appWidgetIds and other badness
728 // lookup also checks that the calling process can access the appWidgetId
729 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
730 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700731 // is anyone listening?
732 if (id.host.callbacks != null) {
733 try {
734 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700735 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700736 } catch (RemoteException e) {
737 // It failed; remove the callback. No need to prune because
738 // we know that this host is still referenced by this instance.
739 id.host.callbacks = null;
740 }
741 }
742 }
743 }
744
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700745 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 List<RemoteViews> updatedViews) {
747 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700748 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
750 host.callbacks = callbacks;
751
752 updatedViews.clear();
753
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700754 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 int N = instances.size();
756 int[] updatedIds = new int[N];
757 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700758 AppWidgetId id = instances.get(i);
759 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 updatedViews.add(id.views);
761 }
762 return updatedIds;
763 }
764 }
765
766 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700767 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700769 if (host != null) {
770 host.callbacks = null;
771 pruneHostLocked(host);
772 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 }
774 }
775
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700776 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700778 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 return true;
780 }
781 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700782 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 return true;
784 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700785 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700787 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 return true;
789 }
790 // Nobody else can access it.
791 return false;
792 }
793
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700794 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700796 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700798 AppWidgetId id = mAppWidgetIds.get(i);
799 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 return id;
801 }
802 }
803 return null;
804 }
805
806 Provider lookupProviderLocked(ComponentName provider) {
Romain Guyd2671e12010-03-11 18:06:42 -0800807 final String className = provider.getClassName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 final int N = mInstalledProviders.size();
809 for (int i=0; i<N; i++) {
810 Provider p = mInstalledProviders.get(i);
Romain Guyd2671e12010-03-11 18:06:42 -0800811 if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 return p;
813 }
814 }
815 return null;
816 }
817
818 Host lookupHostLocked(int uid, int hostId) {
819 final int N = mHosts.size();
820 for (int i=0; i<N; i++) {
821 Host h = mHosts.get(i);
822 if (h.uid == uid && h.hostId == hostId) {
823 return h;
824 }
825 }
826 return null;
827 }
828
829 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
830 final int N = mHosts.size();
831 for (int i=0; i<N; i++) {
832 Host h = mHosts.get(i);
833 if (h.hostId == hostId && h.packageName.equals(packageName)) {
834 return h;
835 }
836 }
837 Host host = new Host();
838 host.packageName = packageName;
839 host.uid = uid;
840 host.hostId = hostId;
841 mHosts.add(host);
842 return host;
843 }
844
845 void pruneHostLocked(Host host) {
846 if (host.instances.size() == 0 && host.callbacks == null) {
847 mHosts.remove(host);
848 }
849 }
850
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700851 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 PackageManager pm = mPackageManager;
853
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700854 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
856 PackageManager.GET_META_DATA);
857
Bjorn Bringert5f857802010-02-10 23:09:48 +0000858 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 for (int i=0; i<N; i++) {
860 ResolveInfo ri = broadcastReceivers.get(i);
861 addProviderLocked(ri);
862 }
863 }
864
865 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800866 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
867 return false;
868 }
869 if (!ri.activityInfo.isEnabled()) {
870 return false;
871 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
873 ri.activityInfo.name), ri);
874 if (p != null) {
875 mInstalledProviders.add(p);
876 return true;
877 } else {
878 return false;
879 }
880 }
881
882 void removeProviderLocked(int index, Provider p) {
883 int N = p.instances.size();
884 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700885 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700887 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 // Stop telling the host about updates for this from now on
889 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700890 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700892 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 id.provider = null;
894 pruneHostLocked(id.host);
895 id.host = null;
896 }
897 p.instances.clear();
898 mInstalledProviders.remove(index);
899 // no need to send the DISABLE broadcast, since the receiver is gone anyway
900 cancelBroadcasts(p);
901 }
902
903 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700904 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 intent.setComponent(p.info.provider);
906 mContext.sendBroadcast(intent);
907 }
908
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700909 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
910 if (appWidgetIds != null && appWidgetIds.length > 0) {
911 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
912 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 intent.setComponent(p.info.provider);
914 mContext.sendBroadcast(intent);
915 }
916 }
917
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700918 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 if (p.info.updatePeriodMillis > 0) {
920 // if this is the first instance, set the alarm. otherwise,
921 // rely on the fact that we've already set it and that
922 // PendingIntent.getBroadcast will update the extras.
923 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700924 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
925 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 intent.setComponent(p.info.provider);
927 long token = Binder.clearCallingIdentity();
928 try {
929 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
930 PendingIntent.FLAG_UPDATE_CURRENT);
931 } finally {
932 Binder.restoreCallingIdentity(token);
933 }
934 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700935 long period = p.info.updatePeriodMillis;
936 if (period < MIN_UPDATE_PERIOD) {
937 period = MIN_UPDATE_PERIOD;
938 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700940 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 }
942 }
943 }
944
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700945 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700947 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700949 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700951 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 }
953
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700954 public int[] getAppWidgetIds(ComponentName provider) {
955 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 Provider p = lookupProviderLocked(provider);
957 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700958 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 } else {
960 return new int[0];
961 }
962 }
963 }
964
965 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
966 Provider p = null;
967
968 ActivityInfo activityInfo = ri.activityInfo;
969 XmlResourceParser parser = null;
970 try {
971 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700972 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800974 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700975 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 return null;
977 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800978
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -0800980
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 int type;
982 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
983 && type != XmlPullParser.START_TAG) {
984 // drain whitespace, comments, etc.
985 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800986
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700988 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800989 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700990 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800991 return null;
992 }
993
994 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700995 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
Romain Guyd2671e12010-03-11 18:06:42 -0800996 // If metaData was null, we would have returned earlier when getting
997 // the parser No need to do the check here
998 info.oldName = activityInfo.metaData.getString(
999 AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000
1001 info.provider = component;
1002 p.uid = activityInfo.applicationInfo.uid;
1003
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001004 Resources res = mPackageManager.getResourcesForApplication(
1005 activityInfo.applicationInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001006
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001007 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001008 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001009
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001010 // These dimensions has to be resolved in the application's context.
1011 // We simply send back the raw complex data, which will be
1012 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1013 TypedValue value = sa.peekValue(
1014 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1015 info.minWidth = value != null ? value.data : 0;
1016 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1017 info.minHeight = value != null ? value.data : 0;
Adam Cohend2e20de2011-02-25 12:03:37 -08001018
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001020 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001022 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001024 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 if (className != null) {
1026 info.configure = new ComponentName(component.getPackageName(), className);
1027 }
1028 info.label = activityInfo.loadLabel(mPackageManager).toString();
1029 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001030 info.previewImage = sa.getResourceId(
1031 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -07001032 info.autoAdvanceViewId = sa.getResourceId(
1033 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Adam Cohen9611f2e2011-02-28 13:39:38 -08001034 info.resizeMode = sa.getInt(
Adam Cohend2e20de2011-02-25 12:03:37 -08001035 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1036 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001037
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 sa.recycle();
1039 } catch (Exception e) {
1040 // Ok to catch Exception here, because anything going wrong because
1041 // of what a client process passes to us should not be fatal for the
1042 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001043 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 return null;
1045 } finally {
1046 if (parser != null) parser.close();
1047 }
1048 return p;
1049 }
1050
1051 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1052 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1053 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1054 throw new PackageManager.NameNotFoundException();
1055 }
1056 return pkgInfo.applicationInfo.uid;
1057 }
1058
1059 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1060 int callingUid = getCallingUid();
1061 int packageUid;
1062 try {
1063 packageUid = getUidForPackage(packageName);
1064 } catch (PackageManager.NameNotFoundException ex) {
1065 throw new IllegalArgumentException("packageName and uid don't match packageName="
1066 + packageName);
1067 }
Marco Nelissen54796e72009-04-30 15:16:30 -07001068 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 throw new IllegalArgumentException("packageName and uid don't match packageName="
1070 + packageName);
1071 }
1072 return callingUid;
1073 }
1074
1075 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001076 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 final int N = mInstalledProviders.size();
1078 for (int i=0; i<N; i++) {
1079 Provider p = mInstalledProviders.get(i);
1080 if (p.instances.size() > 0) {
1081 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001082 int[] appWidgetIds = getAppWidgetIds(p);
1083 sendUpdateIntentLocked(p, appWidgetIds);
1084 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 }
1086 }
1087 }
1088 }
1089
1090 // only call from initialization -- it assumes that the data structures are all empty
1091 void loadStateLocked() {
1092 File temp = savedStateTempFile();
1093 File real = savedStateRealFile();
1094
1095 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1096 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1097 // fully written and delete it.
1098 if (real.exists()) {
1099 readStateFromFileLocked(real);
1100 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001101 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 temp.delete();
1103 }
1104 } else if (temp.exists()) {
1105 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001106 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 temp.renameTo(real);
1108 }
1109 }
1110
1111 void saveStateLocked() {
1112 File temp = savedStateTempFile();
1113 File real = savedStateRealFile();
1114
1115 if (!real.exists()) {
1116 // If the real one doesn't exist, it's either because this is the first time
1117 // or because something went wrong while copying them. In this case, we can't
1118 // trust anything that's in temp. In order to have the loadState code not
1119 // use the temporary one until it's fully written, create an empty file
1120 // for real, which will we'll shortly delete.
1121 try {
Romain Guya5475592009-07-01 17:20:08 -07001122 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001123 real.createNewFile();
1124 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001125 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 }
1127 }
1128
1129 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001130 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 temp.delete();
1132 }
1133
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001134 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001135 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001136 return;
1137 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138
Romain Guya5475592009-07-01 17:20:08 -07001139 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001141 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 temp.renameTo(real);
1143 }
1144
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001145 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 FileOutputStream stream = null;
1147 int N;
1148
1149 try {
1150 stream = new FileOutputStream(file, false);
1151 XmlSerializer out = new FastXmlSerializer();
1152 out.setOutput(stream, "utf-8");
1153 out.startDocument(null, true);
1154
1155
1156 out.startTag(null, "gs");
1157
1158 int providerIndex = 0;
1159 N = mInstalledProviders.size();
1160 for (int i=0; i<N; i++) {
1161 Provider p = mInstalledProviders.get(i);
1162 if (p.instances.size() > 0) {
1163 out.startTag(null, "p");
1164 out.attribute(null, "pkg", p.info.provider.getPackageName());
1165 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001166 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 p.tag = providerIndex;
1168 providerIndex++;
1169 }
1170 }
1171
1172 N = mHosts.size();
1173 for (int i=0; i<N; i++) {
1174 Host host = mHosts.get(i);
1175 out.startTag(null, "h");
1176 out.attribute(null, "pkg", host.packageName);
1177 out.attribute(null, "id", Integer.toHexString(host.hostId));
1178 out.endTag(null, "h");
1179 host.tag = i;
1180 }
1181
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001182 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001184 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001186 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1188 if (id.provider != null) {
1189 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1190 }
1191 out.endTag(null, "g");
1192 }
1193
1194 out.endTag(null, "gs");
1195
1196 out.endDocument();
1197 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001198 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199 } catch (IOException e) {
1200 try {
1201 if (stream != null) {
1202 stream.close();
1203 }
1204 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001205 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 }
1207 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001208 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209 file.delete();
1210 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001211 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 }
1213 }
1214
1215 void readStateFromFileLocked(File file) {
1216 FileInputStream stream = null;
1217
1218 boolean success = false;
1219
1220 try {
1221 stream = new FileInputStream(file);
1222 XmlPullParser parser = Xml.newPullParser();
1223 parser.setInput(stream, null);
1224
1225 int type;
1226 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001227 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 do {
1229 type = parser.next();
1230 if (type == XmlPullParser.START_TAG) {
1231 String tag = parser.getName();
1232 if ("p".equals(tag)) {
1233 // TODO: do we need to check that this package has the same signature
1234 // as before?
1235 String pkg = parser.getAttributeValue(null, "pkg");
1236 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c2010-03-11 15:30:02 -08001237
1238 final PackageManager packageManager = mContext.getPackageManager();
1239 try {
1240 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1241 } catch (PackageManager.NameNotFoundException e) {
1242 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1243 new String[] { pkg });
1244 pkg = pkgs[0];
1245 }
1246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1248 if (p == null && mSafeMode) {
1249 // if we're in safe mode, make a temporary one
1250 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001251 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252 p.info.provider = new ComponentName(pkg, cl);
1253 p.zombie = true;
1254 mInstalledProviders.add(p);
1255 }
1256 if (p != null) {
1257 // if it wasn't uninstalled or something
1258 loadedProviders.put(providerIndex, p);
1259 }
1260 providerIndex++;
1261 }
1262 else if ("h".equals(tag)) {
1263 Host host = new Host();
1264
1265 // TODO: do we need to check that this package has the same signature
1266 // as before?
1267 host.packageName = parser.getAttributeValue(null, "pkg");
1268 try {
1269 host.uid = getUidForPackage(host.packageName);
1270 } catch (PackageManager.NameNotFoundException ex) {
1271 host.zombie = true;
1272 }
1273 if (!host.zombie || mSafeMode) {
1274 // In safe mode, we don't discard the hosts we don't recognize
1275 // so that they're not pruned from our list. Otherwise, we do.
1276 host.hostId = Integer.parseInt(
1277 parser.getAttributeValue(null, "id"), 16);
1278 mHosts.add(host);
1279 }
1280 }
1281 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001282 AppWidgetId id = new AppWidgetId();
1283 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1284 if (id.appWidgetId >= mNextAppWidgetId) {
1285 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287
1288 String providerString = parser.getAttributeValue(null, "p");
1289 if (providerString != null) {
1290 // there's no provider if it hasn't been bound yet.
1291 // maybe we don't have to save this, but it brings the system
1292 // to the state it was in.
1293 int pIndex = Integer.parseInt(providerString, 16);
1294 id.provider = loadedProviders.get(pIndex);
1295 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001296 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001297 + pIndex + " which is " + id.provider);
1298 }
1299 if (id.provider == null) {
1300 // This provider is gone. We just let the host figure out
1301 // that this happened when it fails to load it.
1302 continue;
1303 }
1304 }
1305
1306 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1307 id.host = mHosts.get(hIndex);
1308 if (id.host == null) {
1309 // This host is gone.
1310 continue;
1311 }
1312
1313 if (id.provider != null) {
1314 id.provider.instances.add(id);
1315 }
1316 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001317 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001318 }
1319 }
1320 } while (type != XmlPullParser.END_DOCUMENT);
1321 success = true;
1322 } catch (NullPointerException 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 (NumberFormatException 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 (XmlPullParserException 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 (IOException 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 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001331 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 }
1333 try {
1334 if (stream != null) {
1335 stream.close();
1336 }
1337 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001338 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 }
1340
1341 if (success) {
1342 // delete any hosts that didn't manage to get connected (should happen)
1343 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001344 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 pruneHostLocked(mHosts.get(i));
1346 }
1347 } else {
1348 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001349 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 mHosts.clear();
1351 final int N = mInstalledProviders.size();
1352 for (int i=0; i<N; i++) {
1353 mInstalledProviders.get(i).instances.clear();
1354 }
1355 }
1356 }
1357
1358 File savedStateTempFile() {
1359 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1360 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1361 }
1362
1363 File savedStateRealFile() {
1364 return new File("/data/system/" + SETTINGS_FILENAME);
1365 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1366 }
1367
1368 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1369 public void onReceive(Context context, Intent intent) {
1370 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001371 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1373 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001374 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1375 Locale revised = Locale.getDefault();
1376 if (revised == null || mLocale == null ||
1377 !(revised.equals(mLocale))) {
1378 mLocale = revised;
1379
1380 synchronized (mAppWidgetIds) {
1381 int N = mInstalledProviders.size();
1382 for (int i=N-1; i>=0; i--) {
1383 Provider p = mInstalledProviders.get(i);
1384 String pkgName = p.info.provider.getPackageName();
1385 updateProvidersForPackageLocked(pkgName);
1386 }
1387 saveStateLocked();
1388 }
1389 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001391 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001392 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001393 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001394 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001395 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1396 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001397 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001398 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1399 added = false;
1400 } else {
1401 Uri uri = intent.getData();
1402 if (uri == null) {
1403 return;
1404 }
1405 String pkgName = uri.getSchemeSpecificPart();
1406 if (pkgName == null) {
1407 return;
1408 }
1409 pkgList = new String[] { pkgName };
1410 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001411 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001412 }
1413 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 return;
1415 }
Joe Onoratod070e892011-01-07 20:50:37 -08001416 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001417 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001418 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001419 if (changed || (extras != null &&
1420 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001421 for (String pkgName : pkgList) {
1422 // The package was just upgraded
1423 updateProvidersForPackageLocked(pkgName);
1424 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001425 } else {
1426 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001427 for (String pkgName : pkgList) {
1428 addProvidersForPackageLocked(pkgName);
1429 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001430 }
1431 saveStateLocked();
1432 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001433 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001434 Bundle extras = intent.getExtras();
1435 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1436 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1437 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001438 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001439 for (String pkgName : pkgList) {
1440 removeProvidersForPackageLocked(pkgName);
1441 saveStateLocked();
1442 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001443 }
1444 }
1445 }
1446 }
1447 }
1448 };
1449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001451 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001452 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1454 PackageManager.GET_META_DATA);
1455
Bjorn Bringert5f857802010-02-10 23:09:48 +00001456 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457 for (int i=0; i<N; i++) {
1458 ResolveInfo ri = broadcastReceivers.get(i);
1459 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc2010-08-24 17:02:09 -04001460 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1461 continue;
1462 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001463 if (pkgName.equals(ai.packageName)) {
1464 addProviderLocked(ri);
1465 }
1466 }
1467 }
1468
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001470 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001471 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001472 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001473 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1474 PackageManager.GET_META_DATA);
1475
1476 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001477 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 for (int i=0; i<N; i++) {
1479 ResolveInfo ri = broadcastReceivers.get(i);
1480 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc2010-08-24 17:02:09 -04001481 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1482 continue;
1483 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 if (pkgName.equals(ai.packageName)) {
1485 ComponentName component = new ComponentName(ai.packageName, ai.name);
1486 Provider p = lookupProviderLocked(component);
1487 if (p == null) {
1488 if (addProviderLocked(ri)) {
1489 keep.add(ai.name);
1490 }
1491 } else {
1492 Provider parsed = parseProviderInfoXml(component, ri);
1493 if (parsed != null) {
1494 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001495 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 p.info = parsed.info;
1497 // If it's enabled
1498 final int M = p.instances.size();
1499 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001500 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501 // Reschedule for the new updatePeriodMillis (don't worry about handling
1502 // it specially if updatePeriodMillis didn't change because we just sent
1503 // an update, and the next one will be updatePeriodMillis from now).
1504 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001505 registerForBroadcastsLocked(p, appWidgetIds);
1506 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001508 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001509 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 if (id.host != null && id.host.callbacks != null) {
1511 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001512 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 } catch (RemoteException ex) {
1514 // It failed; remove the callback. No need to prune because
1515 // we know that this host is still referenced by this
1516 // instance.
1517 id.host.callbacks = null;
1518 }
1519 }
1520 }
1521 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001522 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001523 }
1524 }
1525 }
1526 }
1527 }
1528
1529 // prune the ones we don't want to keep
1530 N = mInstalledProviders.size();
1531 for (int i=N-1; i>=0; i--) {
1532 Provider p = mInstalledProviders.get(i);
1533 if (pkgName.equals(p.info.provider.getPackageName())
1534 && !keep.contains(p.info.provider.getClassName())) {
1535 removeProviderLocked(i, p);
1536 }
1537 }
1538 }
1539
1540 void removeProvidersForPackageLocked(String pkgName) {
1541 int N = mInstalledProviders.size();
1542 for (int i=N-1; i>=0; i--) {
1543 Provider p = mInstalledProviders.get(i);
1544 if (pkgName.equals(p.info.provider.getPackageName())) {
1545 removeProviderLocked(i, p);
1546 }
1547 }
1548
1549 // Delete the hosts for this package too
1550 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001551 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552 // so we don't need to worry about sending DISABLE broadcasts to them.
1553 N = mHosts.size();
1554 for (int i=N-1; i>=0; i--) {
1555 Host host = mHosts.get(i);
1556 if (pkgName.equals(host.packageName)) {
1557 deleteHostLocked(host);
1558 }
1559 }
1560 }
1561}
1562