blob: dd649e7e6061b72a4aa5afcfc21975b6a4a8a6fe [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
Dianne Hackborn2f0b1752011-05-31 17:59:49 -070032import org.apache.commons.logging.impl.SimpleLog;
Winson Chung81f39eb2011-01-11 18:05:01 -080033import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35import org.xmlpull.v1.XmlSerializer;
36
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.app.AlarmManager;
38import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070039import android.appwidget.AppWidgetManager;
40import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.content.BroadcastReceiver;
42import android.content.ComponentName;
43import android.content.Context;
44import android.content.Intent;
Winson Chung81f39eb2011-01-11 18:05:01 -080045import android.content.Intent.FilterComparison;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.content.IntentFilter;
Winson Chung81f39eb2011-01-11 18:05:01 -080047import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.content.pm.ActivityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -040049import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.content.pm.PackageInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080051import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.content.pm.ResolveInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080053import android.content.pm.ServiceInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080054import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.content.res.TypedArray;
56import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.net.Uri;
58import android.os.Binder;
59import android.os.Bundle;
Winson Chung81f39eb2011-01-11 18:05:01 -080060import android.os.IBinder;
Marco Nelissen54796e72009-04-30 15:16:30 -070061import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.os.RemoteException;
63import android.os.SystemClock;
64import android.util.AttributeSet;
Winson Chung16c8d8a2011-01-20 16:19:33 -080065import android.util.Log;
Winson Chung81f39eb2011-01-11 18:05:01 -080066import android.util.Pair;
Joe Onorato8a9b2202010-02-26 18:56:32 -080067import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070068import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.util.Xml;
70import android.widget.RemoteViews;
Winson Chung84bbb022011-02-21 13:57:45 -080071import android.widget.RemoteViewsService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070073import com.android.internal.appwidget.IAppWidgetHost;
Winson Chung81f39eb2011-01-11 18:05:01 -080074import com.android.internal.appwidget.IAppWidgetService;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080075import com.android.internal.util.FastXmlSerializer;
Winson Chung81f39eb2011-01-11 18:05:01 -080076import com.android.internal.widget.IRemoteViewsAdapterConnection;
Winson Chung84bbb022011-02-21 13:57:45 -080077import com.android.internal.widget.IRemoteViewsFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070079class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070081 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070083 private static final String SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070085 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
87 /*
88 * When identifying a Host or Provider based on the calling process, use the uid field.
89 * When identifying a Host or Provider based on a package manager broadcast, use the
90 * package given.
91 */
92
93 static class Provider {
94 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070095 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070096 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 PendingIntent broadcast;
98 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
99
100 int tag; // for use while saving state (the index)
101 }
102
103 static class Host {
104 int uid;
105 int hostId;
106 String packageName;
Romain Guya5475592009-07-01 17:20:08 -0700107 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700108 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
110
111 int tag; // for use while saving state (the index)
112 }
113
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700114 static class AppWidgetId {
115 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 Provider provider;
117 RemoteViews views;
118 Host host;
119 }
120
Winson Chung81f39eb2011-01-11 18:05:01 -0800121 /**
122 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
123 * This needs to be a static inner class since a reference to the ServiceConnection is held
124 * globally and may lead us to leak AppWidgetService instances (if there were more than one).
125 */
126 static class ServiceConnectionProxy implements ServiceConnection {
Winson Chung81f39eb2011-01-11 18:05:01 -0800127 private final Pair<Integer, Intent.FilterComparison> mKey;
128 private final IBinder mConnectionCb;
129
Winson Chung16c8d8a2011-01-20 16:19:33 -0800130 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800131 mKey = key;
132 mConnectionCb = connectionCb;
133 }
134 public void onServiceConnected(ComponentName name, IBinder service) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800135 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800136 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
137 try {
138 cb.onServiceConnected(service);
Adam Cohenc2be22c2011-03-16 16:33:53 -0700139 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800140 e.printStackTrace();
141 }
142 }
143 public void onServiceDisconnected(ComponentName name) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800144 disconnect();
145 }
146 public void disconnect() {
147 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800148 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
149 try {
150 cb.onServiceDisconnected();
Adam Cohenc2be22c2011-03-16 16:33:53 -0700151 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800152 e.printStackTrace();
153 }
154 }
155 }
156
Winson Chung84bbb022011-02-21 13:57:45 -0800157 // Manages active connections to RemoteViewsServices
Winson Chung81f39eb2011-01-11 18:05:01 -0800158 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
159 mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
Winson Chung84bbb022011-02-21 13:57:45 -0800160 // Manages persistent references to RemoteViewsServices from different App Widgets
161 private final HashMap<FilterComparison, HashSet<Integer>>
162 mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
Winson Chung81f39eb2011-01-11 18:05:01 -0800163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700165 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 PackageManager mPackageManager;
167 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700168 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700169 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700170 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
171 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 boolean mSafeMode;
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700173 boolean mStateLoaded;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174
Adam Cohen7bb98832011-10-05 18:10:13 -0700175 // These are for debugging only -- widgets are going missing in some rare instances
176 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
177 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
178
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700179 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 mContext = context;
181 mPackageManager = context.getPackageManager();
182 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
183 }
184
185 public void systemReady(boolean safeMode) {
186 mSafeMode = safeMode;
187
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700188 synchronized (mAppWidgetIds) {
189 ensureStateLoadedLocked();
190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191
192 // Register for the boot completed broadcast, so we can send the
193 // ENABLE broacasts. If we try to send them now, they time out,
194 // because the system isn't ready to handle them yet.
195 mContext.registerReceiver(mBroadcastReceiver,
196 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
197
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700198 // Register for configuration changes so we can update the names
199 // of the widgets when the locale changes.
200 mContext.registerReceiver(mBroadcastReceiver,
201 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 // Register for broadcasts about package install, etc., so we can
204 // update the provider list.
205 IntentFilter filter = new IntentFilter();
206 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
Joe Onoratod070e892011-01-07 20:50:37 -0800207 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
209 filter.addDataScheme("package");
210 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800211 // Register for events related to sdcard installation.
212 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800213 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
214 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800215 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 }
217
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700218 private void ensureStateLoadedLocked() {
219 if (!mStateLoaded) {
220 loadAppWidgetList();
221 loadStateLocked();
222 mStateLoaded = true;
223 }
224 }
225
Adam Cohen7bb98832011-10-05 18:10:13 -0700226 private void dumpProvider(Provider p, int index, PrintWriter pw) {
227 AppWidgetProviderInfo info = p.info;
228 pw.print(" ["); pw.print(index); pw.print("] provider ");
229 pw.print(info.provider.flattenToShortString());
230 pw.println(':');
231 pw.print(" min=("); pw.print(info.minWidth);
232 pw.print("x"); pw.print(info.minHeight);
233 pw.print(") minResize=("); pw.print(info.minResizeWidth);
234 pw.print("x"); pw.print(info.minResizeHeight);
235 pw.print(") updatePeriodMillis=");
236 pw.print(info.updatePeriodMillis);
237 pw.print(" resizeMode=");
238 pw.print(info.resizeMode);
239 pw.print(" autoAdvanceViewId=");
240 pw.print(info.autoAdvanceViewId);
241 pw.print(" initialLayout=#");
242 pw.print(Integer.toHexString(info.initialLayout));
243 pw.print(" zombie="); pw.println(p.zombie);
244 }
245
246 private void dumpHost(Host host, int index, PrintWriter pw) {
247 pw.print(" ["); pw.print(index); pw.print("] hostId=");
248 pw.print(host.hostId); pw.print(' ');
249 pw.print(host.packageName); pw.print('/');
250 pw.print(host.uid); pw.println(':');
251 pw.print(" callbacks="); pw.println(host.callbacks);
252 pw.print(" instances.size="); pw.print(host.instances.size());
253 pw.print(" zombie="); pw.println(host.zombie);
254 }
255
256 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
257 pw.print(" ["); pw.print(index); pw.print("] id=");
258 pw.println(id.appWidgetId);
259 pw.print(" hostId=");
260 pw.print(id.host.hostId); pw.print(' ');
261 pw.print(id.host.packageName); pw.print('/');
262 pw.println(id.host.uid);
263 if (id.provider != null) {
264 pw.print(" provider=");
265 pw.println(id.provider.info.provider.flattenToShortString());
266 }
267 if (id.host != null) {
268 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
269 }
270 if (id.views != null) {
271 pw.print(" views="); pw.println(id.views);
272 }
273 }
274
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 @Override
276 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
277 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
278 != PackageManager.PERMISSION_GRANTED) {
279 pw.println("Permission Denial: can't dump from from pid="
280 + Binder.getCallingPid()
281 + ", uid=" + Binder.getCallingUid());
282 return;
283 }
284
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700285 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700287 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700289 dumpProvider(mInstalledProviders.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 }
291
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700292 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700293 pw.println(" ");
294 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700296 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 }
298
299 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700300 pw.println(" ");
301 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700303 dumpHost(mHosts.get(i), i, pw);
304 }
305
306 N = mDeletedProviders.size();
307 pw.println(" ");
308 pw.println("Deleted Providers:");
309 for (int i=0; i<N; i++) {
310 dumpProvider(mDeletedProviders.get(i), i, pw);
311 }
312
313 N = mDeletedHosts.size();
314 pw.println(" ");
315 pw.println("Deleted Hosts:");
316 for (int i=0; i<N; i++) {
317 dumpHost(mDeletedHosts.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 }
319 }
320 }
321
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700322 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700324 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700325 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700326 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327
328 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
329
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700330 AppWidgetId id = new AppWidgetId();
331 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 id.host = host;
333
334 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700335 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336
337 saveStateLocked();
338
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700339 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 }
341 }
342
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700343 public void deleteAppWidgetId(int appWidgetId) {
344 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700345 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700346 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700348 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 saveStateLocked();
350 }
351 }
352 }
353
354 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700355 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700356 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 int callingUid = getCallingUid();
358 Host host = lookupHostLocked(callingUid, hostId);
359 if (host != null) {
360 deleteHostLocked(host);
361 saveStateLocked();
362 }
363 }
364 }
365
366 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700367 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700368 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 int callingUid = getCallingUid();
370 final int N = mHosts.size();
371 boolean changed = false;
372 for (int i=N-1; i>=0; i--) {
373 Host host = mHosts.get(i);
374 if (host.uid == callingUid) {
375 deleteHostLocked(host);
376 changed = true;
377 }
378 }
379 if (changed) {
380 saveStateLocked();
381 }
382 }
383 }
384
385 void deleteHostLocked(Host host) {
386 final int N = host.instances.size();
387 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700388 AppWidgetId id = host.instances.get(i);
389 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 }
391 host.instances.clear();
392 mHosts.remove(host);
Adam Cohen7bb98832011-10-05 18:10:13 -0700393 mDeletedHosts.add(host);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 // it's gone or going away, abruptly drop the callback connection
395 host.callbacks = null;
396 }
397
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700398 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800399 // We first unbind all services that are bound to this id
400 unbindAppWidgetRemoteViewsServicesLocked(id);
401
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 Host host = id.host;
403 host.instances.remove(id);
404 pruneHostLocked(host);
405
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700406 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407
408 Provider p = id.provider;
409 if (p != null) {
410 p.instances.remove(id);
411 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700412 // send the broacast saying that this appWidgetId has been deleted
413 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700415 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 mContext.sendBroadcast(intent);
417 if (p.instances.size() == 0) {
418 // cancel the future updates
419 cancelBroadcasts(p);
420
421 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700422 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 intent.setComponent(p.info.provider);
424 mContext.sendBroadcast(intent);
425 }
426 }
427 }
428 }
429
430 void cancelBroadcasts(Provider p) {
431 if (p.broadcast != null) {
432 mAlarmManager.cancel(p.broadcast);
433 long token = Binder.clearCallingIdentity();
434 try {
435 p.broadcast.cancel();
436 } finally {
437 Binder.restoreCallingIdentity(token);
438 }
439 p.broadcast = null;
440 }
441 }
442
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700443 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
444 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
445 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
Dianne Hackborn4912f162011-06-15 17:40:44 -0700446
447 final long ident = Binder.clearCallingIdentity();
448 try {
449 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700450 ensureStateLoadedLocked();
Dianne Hackborn4912f162011-06-15 17:40:44 -0700451 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
452 if (id == null) {
453 throw new IllegalArgumentException("bad appWidgetId");
454 }
455 if (id.provider != null) {
456 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
457 + id.provider.info.provider);
458 }
459 Provider p = lookupProviderLocked(provider);
460 if (p == null) {
461 throw new IllegalArgumentException("not a appwidget provider: " + provider);
462 }
463 if (p.zombie) {
464 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
465 + " safe mode: " + provider);
466 }
467
468 id.provider = p;
469 p.instances.add(id);
470 int instancesSize = p.instances.size();
471 if (instancesSize == 1) {
472 // tell the provider that it's ready
473 sendEnableIntentLocked(p);
474 }
475
476 // send an update now -- We need this update now, and just for this appWidgetId.
477 // It's less critical when the next one happens, so when we schdule the next one,
478 // we add updatePeriodMillis to its start time. That time will have some slop,
479 // but that's okay.
480 sendUpdateIntentLocked(p, new int[] { appWidgetId });
481
482 // schedule the future updates
483 registerForBroadcastsLocked(p, getAppWidgetIds(p));
484 saveStateLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 }
Dianne Hackborn4912f162011-06-15 17:40:44 -0700486 } finally {
487 Binder.restoreCallingIdentity(ident);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 }
489 }
490
Winson Chung84bbb022011-02-21 13:57:45 -0800491 // Binds to a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800492 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
493 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700494 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800495 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
496 if (id == null) {
497 throw new IllegalArgumentException("bad appWidgetId");
498 }
499 final ComponentName componentName = intent.getComponent();
500 try {
501 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
502 PackageManager.GET_PERMISSIONS);
503 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
504 throw new SecurityException("Selected service does not require "
505 + android.Manifest.permission.BIND_REMOTEVIEWS
506 + ": " + componentName);
507 }
508 } catch (PackageManager.NameNotFoundException e) {
509 throw new IllegalArgumentException("Unknown component " + componentName);
510 }
511
Winson Chung16c8d8a2011-01-20 16:19:33 -0800512 // If there is already a connection made for this service intent, then disconnect from
513 // that first. (This does not allow multiple connections to the same service under
514 // the same key)
515 ServiceConnectionProxy conn = null;
Winson Chung84bbb022011-02-21 13:57:45 -0800516 FilterComparison fc = new FilterComparison(intent);
517 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800518 if (mBoundRemoteViewsServices.containsKey(key)) {
519 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
520 conn.disconnect();
521 mContext.unbindService(conn);
522 mBoundRemoteViewsServices.remove(key);
523 }
524
525 // Bind to the RemoteViewsService (which will trigger a callback to the
526 // RemoteViewsAdapter.onServiceConnected())
Winson Chung81f39eb2011-01-11 18:05:01 -0800527 final long token = Binder.clearCallingIdentity();
528 try {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800529 conn = new ServiceConnectionProxy(key, connection);
Winson Chung81f39eb2011-01-11 18:05:01 -0800530 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
531 mBoundRemoteViewsServices.put(key, conn);
532 } finally {
533 Binder.restoreCallingIdentity(token);
534 }
Winson Chung84bbb022011-02-21 13:57:45 -0800535
536 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
537 // when we can call back to the RemoteViewsService later to destroy associated
538 // factories.
Winson Chung22bc69d2011-02-25 15:13:38 -0800539 incrementAppWidgetServiceRefCount(appWidgetId, fc);
Winson Chung81f39eb2011-01-11 18:05:01 -0800540 }
541 }
542
Winson Chung84bbb022011-02-21 13:57:45 -0800543 // Unbinds from a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800544 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
545 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700546 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800547 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
548 // RemoteViewsAdapter)
549 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
550 new FilterComparison(intent));
551 if (mBoundRemoteViewsServices.containsKey(key)) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800552 // We don't need to use the appWidgetId until after we are sure there is something
553 // to unbind. Note that this may mask certain issues with apps calling unbind()
554 // more than necessary.
555 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
556 if (id == null) {
557 throw new IllegalArgumentException("bad appWidgetId");
558 }
559
560 ServiceConnectionProxy conn =
561 (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
562 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800563 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800564 mBoundRemoteViewsServices.remove(key);
565 } else {
566 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
Winson Chung81f39eb2011-01-11 18:05:01 -0800567 }
568 }
569 }
570
Winson Chung84bbb022011-02-21 13:57:45 -0800571 // Unbinds from a RemoteViewsService when we delete an app widget
Winson Chung81f39eb2011-01-11 18:05:01 -0800572 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800573 int appWidgetId = id.appWidgetId;
574 // Unbind all connections to Services bound to this AppWidgetId
Winson Chung81f39eb2011-01-11 18:05:01 -0800575 Iterator<Pair<Integer, Intent.FilterComparison>> it =
576 mBoundRemoteViewsServices.keySet().iterator();
Winson Chung81f39eb2011-01-11 18:05:01 -0800577 while (it.hasNext()) {
578 final Pair<Integer, Intent.FilterComparison> key = it.next();
579 if (key.first.intValue() == appWidgetId) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800580 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
581 mBoundRemoteViewsServices.get(key);
582 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800583 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800584 it.remove();
Winson Chung81f39eb2011-01-11 18:05:01 -0800585 }
586 }
Winson Chung84bbb022011-02-21 13:57:45 -0800587
588 // Check if we need to destroy any services (if no other app widgets are
589 // referencing the same service)
590 decrementAppWidgetServiceRefCount(appWidgetId);
591 }
592
593 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
594 private void destroyRemoteViewsService(final Intent intent) {
595 final ServiceConnection conn = new ServiceConnection() {
596 @Override
597 public void onServiceConnected(ComponentName name, IBinder service) {
598 final IRemoteViewsFactory cb =
599 IRemoteViewsFactory.Stub.asInterface(service);
600 try {
601 cb.onDestroy(intent);
Adam Cohen2625fea2011-03-23 17:24:30 -0700602 } catch (RemoteException e) {
603 e.printStackTrace();
604 } catch (RuntimeException e) {
Winson Chung84bbb022011-02-21 13:57:45 -0800605 e.printStackTrace();
606 }
607 mContext.unbindService(this);
608 }
609 @Override
610 public void onServiceDisconnected(android.content.ComponentName name) {
611 // Do nothing
612 }
613 };
614
615 // Bind to the service and remove the static intent->factory mapping in the
616 // RemoteViewsService.
617 final long token = Binder.clearCallingIdentity();
618 try {
619 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
620 } finally {
621 Binder.restoreCallingIdentity(token);
622 }
623 }
624
625 // Adds to the ref-count for a given RemoteViewsService intent
626 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
627 HashSet<Integer> appWidgetIds = null;
628 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
629 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
630 } else {
631 appWidgetIds = new HashSet<Integer>();
632 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
633 }
634 appWidgetIds.add(appWidgetId);
635 }
636
637 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
638 // the ref-count reaches zero.
639 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
640 Iterator<FilterComparison> it =
641 mRemoteViewsServicesAppWidgets.keySet().iterator();
642 while (it.hasNext()) {
643 final FilterComparison key = it.next();
644 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
645 if (ids.remove(appWidgetId)) {
646 // If we have removed the last app widget referencing this service, then we
647 // should destroy it and remove it from this set
648 if (ids.isEmpty()) {
649 destroyRemoteViewsService(key.getIntent());
650 it.remove();
651 }
652 }
653 }
Winson Chung81f39eb2011-01-11 18:05:01 -0800654 }
655
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700656 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
657 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700658 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700659 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 if (id != null && id.provider != null && !id.provider.zombie) {
661 return id.provider.info;
662 }
663 return null;
664 }
665 }
666
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700667 public RemoteViews getAppWidgetViews(int appWidgetId) {
668 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700669 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700670 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 if (id != null) {
672 return id.views;
673 }
674 return null;
675 }
676 }
677
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700678 public List<AppWidgetProviderInfo> getInstalledProviders() {
679 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700680 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700682 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 for (int i=0; i<N; i++) {
684 Provider p = mInstalledProviders.get(i);
685 if (!p.zombie) {
686 result.add(p.info);
687 }
688 }
689 return result;
690 }
691 }
692
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700693 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
694 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 return;
696 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700697 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 return;
699 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700700 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700702 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700703 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700705 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
706 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 }
708 }
709 }
710
Adam Cohen2dd21972010-08-15 18:20:04 -0700711 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
712 if (appWidgetIds == null) {
713 return;
714 }
715 if (appWidgetIds.length == 0) {
716 return;
717 }
718 final int N = appWidgetIds.length;
719
720 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700721 ensureStateLoadedLocked();
Adam Cohen2dd21972010-08-15 18:20:04 -0700722 for (int i=0; i<N; i++) {
723 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
724 updateAppWidgetInstanceLocked(id, views, true);
725 }
726 }
727 }
728
Winson Chung6394c0e2010-08-16 10:14:56 -0700729 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700730 if (appWidgetIds == null) {
731 return;
732 }
733 if (appWidgetIds.length == 0) {
734 return;
735 }
736 final int N = appWidgetIds.length;
737
738 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700739 ensureStateLoadedLocked();
Winson Chung499cb9f2010-07-16 11:18:17 -0700740 for (int i=0; i<N; i++) {
741 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700742 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700743 }
744 }
745 }
746
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700747 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
748 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700749 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 Provider p = lookupProviderLocked(provider);
751 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800752 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 return;
754 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700755 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 final int N = instances.size();
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 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 }
761 }
762 }
763
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700764 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700765 updateAppWidgetInstanceLocked(id, views, false);
766 }
767
768 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700769 // allow for stale appWidgetIds and other badness
770 // lookup also checks that the calling process can access the appWidgetId
771 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700773
774 // We do not want to save this RemoteViews
775 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776
777 // is anyone listening?
778 if (id.host.callbacks != null) {
779 try {
780 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700781 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 } catch (RemoteException e) {
783 // It failed; remove the callback. No need to prune because
784 // we know that this host is still referenced by this instance.
785 id.host.callbacks = null;
786 }
787 }
788 }
789 }
790
Winson Chung6394c0e2010-08-16 10:14:56 -0700791 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700792 // allow for stale appWidgetIds and other badness
793 // lookup also checks that the calling process can access the appWidgetId
794 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
795 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700796 // is anyone listening?
797 if (id.host.callbacks != null) {
798 try {
799 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700800 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700801 } catch (RemoteException e) {
802 // It failed; remove the callback. No need to prune because
803 // we know that this host is still referenced by this instance.
804 id.host.callbacks = null;
805 }
806 }
807 }
808 }
809
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700810 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 List<RemoteViews> updatedViews) {
812 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700813 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700814 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
816 host.callbacks = callbacks;
817
818 updatedViews.clear();
819
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700820 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 int N = instances.size();
822 int[] updatedIds = new int[N];
823 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700824 AppWidgetId id = instances.get(i);
825 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 updatedViews.add(id.views);
827 }
828 return updatedIds;
829 }
830 }
831
832 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700833 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700834 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700836 if (host != null) {
837 host.callbacks = null;
838 pruneHostLocked(host);
839 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 }
841 }
842
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700843 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700845 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 return true;
847 }
848 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700849 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 return true;
851 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700852 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700854 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 return true;
856 }
857 // Nobody else can access it.
858 return false;
859 }
860
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700861 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700863 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700865 AppWidgetId id = mAppWidgetIds.get(i);
866 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 return id;
868 }
869 }
870 return null;
871 }
872
873 Provider lookupProviderLocked(ComponentName provider) {
874 final int N = mInstalledProviders.size();
875 for (int i=0; i<N; i++) {
876 Provider p = mInstalledProviders.get(i);
Adam Cohenbac26a122011-08-16 20:42:30 -0700877 if (p.info.provider.equals(provider)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 return p;
879 }
880 }
881 return null;
882 }
883
884 Host lookupHostLocked(int uid, int hostId) {
885 final int N = mHosts.size();
886 for (int i=0; i<N; i++) {
887 Host h = mHosts.get(i);
888 if (h.uid == uid && h.hostId == hostId) {
889 return h;
890 }
891 }
892 return null;
893 }
894
895 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
896 final int N = mHosts.size();
897 for (int i=0; i<N; i++) {
898 Host h = mHosts.get(i);
899 if (h.hostId == hostId && h.packageName.equals(packageName)) {
900 return h;
901 }
902 }
903 Host host = new Host();
904 host.packageName = packageName;
905 host.uid = uid;
906 host.hostId = hostId;
907 mHosts.add(host);
908 return host;
909 }
910
911 void pruneHostLocked(Host host) {
912 if (host.instances.size() == 0 && host.callbacks == null) {
913 mHosts.remove(host);
914 }
915 }
916
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700917 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 PackageManager pm = mPackageManager;
919
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700920 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
922 PackageManager.GET_META_DATA);
923
Bjorn Bringert5f857802010-02-10 23:09:48 +0000924 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 for (int i=0; i<N; i++) {
926 ResolveInfo ri = broadcastReceivers.get(i);
927 addProviderLocked(ri);
928 }
929 }
930
931 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800932 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
933 return false;
934 }
935 if (!ri.activityInfo.isEnabled()) {
936 return false;
937 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
939 ri.activityInfo.name), ri);
940 if (p != null) {
941 mInstalledProviders.add(p);
942 return true;
943 } else {
944 return false;
945 }
946 }
947
948 void removeProviderLocked(int index, Provider p) {
949 int N = p.instances.size();
950 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700951 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700953 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 // Stop telling the host about updates for this from now on
955 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700956 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700958 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 id.provider = null;
960 pruneHostLocked(id.host);
961 id.host = null;
962 }
963 p.instances.clear();
964 mInstalledProviders.remove(index);
Adam Cohen7bb98832011-10-05 18:10:13 -0700965 mDeletedProviders.add(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 // no need to send the DISABLE broadcast, since the receiver is gone anyway
967 cancelBroadcasts(p);
968 }
969
970 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700971 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 intent.setComponent(p.info.provider);
973 mContext.sendBroadcast(intent);
974 }
975
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700976 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
977 if (appWidgetIds != null && appWidgetIds.length > 0) {
978 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
979 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 intent.setComponent(p.info.provider);
981 mContext.sendBroadcast(intent);
982 }
983 }
984
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700985 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 if (p.info.updatePeriodMillis > 0) {
987 // if this is the first instance, set the alarm. otherwise,
988 // rely on the fact that we've already set it and that
989 // PendingIntent.getBroadcast will update the extras.
990 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700991 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
992 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 intent.setComponent(p.info.provider);
994 long token = Binder.clearCallingIdentity();
995 try {
996 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
997 PendingIntent.FLAG_UPDATE_CURRENT);
998 } finally {
999 Binder.restoreCallingIdentity(token);
1000 }
1001 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -07001002 long period = p.info.updatePeriodMillis;
1003 if (period < MIN_UPDATE_PERIOD) {
1004 period = MIN_UPDATE_PERIOD;
1005 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -07001007 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 }
1009 }
1010 }
1011
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001012 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001014 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001016 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001018 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 }
1020
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001021 public int[] getAppWidgetIds(ComponentName provider) {
1022 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001023 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 Provider p = lookupProviderLocked(provider);
1025 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001026 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 } else {
1028 return new int[0];
1029 }
1030 }
1031 }
1032
1033 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1034 Provider p = null;
1035
1036 ActivityInfo activityInfo = ri.activityInfo;
1037 XmlResourceParser parser = null;
1038 try {
1039 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001040 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001042 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001043 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 return null;
1045 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001046
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -08001048
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001049 int type;
1050 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1051 && type != XmlPullParser.START_TAG) {
1052 // drain whitespace, comments, etc.
1053 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001054
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001056 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001057 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001058 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 return null;
1060 }
1061
1062 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001063 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 info.provider = component;
1065 p.uid = activityInfo.applicationInfo.uid;
1066
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001067 Resources res = mPackageManager.getResourcesForApplication(
1068 activityInfo.applicationInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001069
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001070 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001071 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001072
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001073 // These dimensions has to be resolved in the application's context.
1074 // We simply send back the raw complex data, which will be
1075 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1076 TypedValue value = sa.peekValue(
1077 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1078 info.minWidth = value != null ? value.data : 0;
1079 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1080 info.minHeight = value != null ? value.data : 0;
Adam Cohen1bfaf562011-07-19 18:05:33 -07001081 value = sa.peekValue(
1082 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1083 info.minResizeWidth = value != null ? value.data : info.minWidth;
1084 value = sa.peekValue(
1085 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1086 info.minResizeHeight = value != null ? value.data : info.minHeight;
Adam Cohend2e20de2011-02-25 12:03:37 -08001087
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001089 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001091 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001093 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 if (className != null) {
1095 info.configure = new ComponentName(component.getPackageName(), className);
1096 }
1097 info.label = activityInfo.loadLabel(mPackageManager).toString();
1098 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001099 info.previewImage = sa.getResourceId(
1100 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -07001101 info.autoAdvanceViewId = sa.getResourceId(
1102 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Adam Cohen9611f2e2011-02-28 13:39:38 -08001103 info.resizeMode = sa.getInt(
Adam Cohend2e20de2011-02-25 12:03:37 -08001104 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1105 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001106
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 sa.recycle();
1108 } catch (Exception e) {
1109 // Ok to catch Exception here, because anything going wrong because
1110 // of what a client process passes to us should not be fatal for the
1111 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001112 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 return null;
1114 } finally {
1115 if (parser != null) parser.close();
1116 }
1117 return p;
1118 }
1119
1120 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1121 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1122 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1123 throw new PackageManager.NameNotFoundException();
1124 }
1125 return pkgInfo.applicationInfo.uid;
1126 }
1127
1128 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1129 int callingUid = getCallingUid();
1130 int packageUid;
1131 try {
1132 packageUid = getUidForPackage(packageName);
1133 } catch (PackageManager.NameNotFoundException ex) {
1134 throw new IllegalArgumentException("packageName and uid don't match packageName="
1135 + packageName);
1136 }
Jeff Brown10e89712011-07-08 18:52:57 -07001137 if (callingUid != packageUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 throw new IllegalArgumentException("packageName and uid don't match packageName="
1139 + packageName);
1140 }
1141 return callingUid;
1142 }
1143
1144 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001145 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001146 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 final int N = mInstalledProviders.size();
1148 for (int i=0; i<N; i++) {
1149 Provider p = mInstalledProviders.get(i);
1150 if (p.instances.size() > 0) {
1151 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001152 int[] appWidgetIds = getAppWidgetIds(p);
1153 sendUpdateIntentLocked(p, appWidgetIds);
1154 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001155 }
1156 }
1157 }
1158 }
1159
1160 // only call from initialization -- it assumes that the data structures are all empty
1161 void loadStateLocked() {
1162 File temp = savedStateTempFile();
1163 File real = savedStateRealFile();
1164
1165 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1166 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1167 // fully written and delete it.
1168 if (real.exists()) {
1169 readStateFromFileLocked(real);
1170 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001171 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 temp.delete();
1173 }
1174 } else if (temp.exists()) {
1175 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001176 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 temp.renameTo(real);
1178 }
1179 }
1180
1181 void saveStateLocked() {
1182 File temp = savedStateTempFile();
1183 File real = savedStateRealFile();
1184
1185 if (!real.exists()) {
1186 // If the real one doesn't exist, it's either because this is the first time
1187 // or because something went wrong while copying them. In this case, we can't
1188 // trust anything that's in temp. In order to have the loadState code not
1189 // use the temporary one until it's fully written, create an empty file
1190 // for real, which will we'll shortly delete.
1191 try {
Romain Guya5475592009-07-01 17:20:08 -07001192 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193 real.createNewFile();
1194 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001195 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 }
1197 }
1198
1199 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001200 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 temp.delete();
1202 }
1203
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001204 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001205 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001206 return;
1207 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208
Romain Guya5475592009-07-01 17:20:08 -07001209 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001211 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 temp.renameTo(real);
1213 }
1214
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001215 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 FileOutputStream stream = null;
1217 int N;
1218
1219 try {
1220 stream = new FileOutputStream(file, false);
1221 XmlSerializer out = new FastXmlSerializer();
1222 out.setOutput(stream, "utf-8");
1223 out.startDocument(null, true);
1224
1225
1226 out.startTag(null, "gs");
1227
1228 int providerIndex = 0;
1229 N = mInstalledProviders.size();
1230 for (int i=0; i<N; i++) {
1231 Provider p = mInstalledProviders.get(i);
1232 if (p.instances.size() > 0) {
1233 out.startTag(null, "p");
1234 out.attribute(null, "pkg", p.info.provider.getPackageName());
1235 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001236 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237 p.tag = providerIndex;
1238 providerIndex++;
1239 }
1240 }
1241
1242 N = mHosts.size();
1243 for (int i=0; i<N; i++) {
1244 Host host = mHosts.get(i);
1245 out.startTag(null, "h");
1246 out.attribute(null, "pkg", host.packageName);
1247 out.attribute(null, "id", Integer.toHexString(host.hostId));
1248 out.endTag(null, "h");
1249 host.tag = i;
1250 }
1251
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001252 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001254 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001256 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001257 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1258 if (id.provider != null) {
1259 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1260 }
1261 out.endTag(null, "g");
1262 }
1263
1264 out.endTag(null, "gs");
1265
1266 out.endDocument();
1267 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001268 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 } catch (IOException e) {
1270 try {
1271 if (stream != null) {
1272 stream.close();
1273 }
1274 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001275 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001276 }
1277 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001278 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 file.delete();
1280 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001281 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 }
1283 }
1284
1285 void readStateFromFileLocked(File file) {
1286 FileInputStream stream = null;
1287
1288 boolean success = false;
1289
1290 try {
1291 stream = new FileInputStream(file);
1292 XmlPullParser parser = Xml.newPullParser();
1293 parser.setInput(stream, null);
1294
1295 int type;
1296 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001297 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 do {
1299 type = parser.next();
1300 if (type == XmlPullParser.START_TAG) {
1301 String tag = parser.getName();
1302 if ("p".equals(tag)) {
1303 // TODO: do we need to check that this package has the same signature
1304 // as before?
1305 String pkg = parser.getAttributeValue(null, "pkg");
1306 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001307
1308 final PackageManager packageManager = mContext.getPackageManager();
1309 try {
1310 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1311 } catch (PackageManager.NameNotFoundException e) {
1312 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1313 new String[] { pkg });
1314 pkg = pkgs[0];
1315 }
1316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1318 if (p == null && mSafeMode) {
1319 // if we're in safe mode, make a temporary one
1320 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001321 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 p.info.provider = new ComponentName(pkg, cl);
1323 p.zombie = true;
1324 mInstalledProviders.add(p);
1325 }
1326 if (p != null) {
1327 // if it wasn't uninstalled or something
1328 loadedProviders.put(providerIndex, p);
1329 }
1330 providerIndex++;
1331 }
1332 else if ("h".equals(tag)) {
1333 Host host = new Host();
1334
1335 // TODO: do we need to check that this package has the same signature
1336 // as before?
1337 host.packageName = parser.getAttributeValue(null, "pkg");
1338 try {
1339 host.uid = getUidForPackage(host.packageName);
1340 } catch (PackageManager.NameNotFoundException ex) {
1341 host.zombie = true;
1342 }
1343 if (!host.zombie || mSafeMode) {
1344 // In safe mode, we don't discard the hosts we don't recognize
1345 // so that they're not pruned from our list. Otherwise, we do.
1346 host.hostId = Integer.parseInt(
1347 parser.getAttributeValue(null, "id"), 16);
1348 mHosts.add(host);
1349 }
1350 }
1351 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001352 AppWidgetId id = new AppWidgetId();
1353 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1354 if (id.appWidgetId >= mNextAppWidgetId) {
1355 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 }
1357
1358 String providerString = parser.getAttributeValue(null, "p");
1359 if (providerString != null) {
1360 // there's no provider if it hasn't been bound yet.
1361 // maybe we don't have to save this, but it brings the system
1362 // to the state it was in.
1363 int pIndex = Integer.parseInt(providerString, 16);
1364 id.provider = loadedProviders.get(pIndex);
1365 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001366 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 + pIndex + " which is " + id.provider);
1368 }
1369 if (id.provider == null) {
1370 // This provider is gone. We just let the host figure out
1371 // that this happened when it fails to load it.
1372 continue;
1373 }
1374 }
1375
1376 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1377 id.host = mHosts.get(hIndex);
1378 if (id.host == null) {
1379 // This host is gone.
1380 continue;
1381 }
1382
1383 if (id.provider != null) {
1384 id.provider.instances.add(id);
1385 }
1386 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001387 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388 }
1389 }
1390 } while (type != XmlPullParser.END_DOCUMENT);
1391 success = true;
1392 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001393 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001394 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001395 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001397 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001399 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001401 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402 }
1403 try {
1404 if (stream != null) {
1405 stream.close();
1406 }
1407 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001408 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409 }
1410
1411 if (success) {
1412 // delete any hosts that didn't manage to get connected (should happen)
1413 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001414 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001415 pruneHostLocked(mHosts.get(i));
1416 }
1417 } else {
1418 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001419 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001420 mHosts.clear();
1421 final int N = mInstalledProviders.size();
1422 for (int i=0; i<N; i++) {
1423 mInstalledProviders.get(i).instances.clear();
1424 }
1425 }
1426 }
1427
1428 File savedStateTempFile() {
1429 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1430 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1431 }
1432
1433 File savedStateRealFile() {
1434 return new File("/data/system/" + SETTINGS_FILENAME);
1435 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1436 }
1437
1438 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1439 public void onReceive(Context context, Intent intent) {
1440 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001441 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1443 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001444 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1445 Locale revised = Locale.getDefault();
1446 if (revised == null || mLocale == null ||
1447 !(revised.equals(mLocale))) {
1448 mLocale = revised;
1449
1450 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001451 ensureStateLoadedLocked();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001452 int N = mInstalledProviders.size();
1453 for (int i=N-1; i>=0; i--) {
1454 Provider p = mInstalledProviders.get(i);
1455 String pkgName = p.info.provider.getPackageName();
1456 updateProvidersForPackageLocked(pkgName);
1457 }
1458 saveStateLocked();
1459 }
1460 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001462 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001463 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001464 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001465 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001466 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1467 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001468 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001469 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1470 added = false;
1471 } else {
1472 Uri uri = intent.getData();
1473 if (uri == null) {
1474 return;
1475 }
1476 String pkgName = uri.getSchemeSpecificPart();
1477 if (pkgName == null) {
1478 return;
1479 }
1480 pkgList = new String[] { pkgName };
1481 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001482 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001483 }
1484 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 return;
1486 }
Joe Onoratod070e892011-01-07 20:50:37 -08001487 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001488 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001489 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001491 if (changed || (extras != null &&
1492 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001493 for (String pkgName : pkgList) {
1494 // The package was just upgraded
1495 updateProvidersForPackageLocked(pkgName);
1496 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001497 } else {
1498 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001499 for (String pkgName : pkgList) {
1500 addProvidersForPackageLocked(pkgName);
1501 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 }
1503 saveStateLocked();
1504 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001505 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001506 Bundle extras = intent.getExtras();
1507 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1508 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1509 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001510 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001511 ensureStateLoadedLocked();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001512 for (String pkgName : pkgList) {
1513 removeProvidersForPackageLocked(pkgName);
1514 saveStateLocked();
1515 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001516 }
1517 }
1518 }
1519 }
1520 }
1521 };
1522
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001523 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001524 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001525 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1527 PackageManager.GET_META_DATA);
1528
Bjorn Bringert5f857802010-02-10 23:09:48 +00001529 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 for (int i=0; i<N; i++) {
1531 ResolveInfo ri = broadcastReceivers.get(i);
1532 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001533 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1534 continue;
1535 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 if (pkgName.equals(ai.packageName)) {
1537 addProviderLocked(ri);
1538 }
1539 }
1540 }
1541
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001542 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001543 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001544 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001545 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001546 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1547 PackageManager.GET_META_DATA);
1548
1549 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001550 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 for (int i=0; i<N; i++) {
1552 ResolveInfo ri = broadcastReceivers.get(i);
1553 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001554 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1555 continue;
1556 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 if (pkgName.equals(ai.packageName)) {
1558 ComponentName component = new ComponentName(ai.packageName, ai.name);
1559 Provider p = lookupProviderLocked(component);
1560 if (p == null) {
1561 if (addProviderLocked(ri)) {
1562 keep.add(ai.name);
1563 }
1564 } else {
1565 Provider parsed = parseProviderInfoXml(component, ri);
1566 if (parsed != null) {
1567 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001568 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001569 p.info = parsed.info;
1570 // If it's enabled
1571 final int M = p.instances.size();
1572 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001573 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001574 // Reschedule for the new updatePeriodMillis (don't worry about handling
1575 // it specially if updatePeriodMillis didn't change because we just sent
1576 // an update, and the next one will be updatePeriodMillis from now).
1577 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001578 registerForBroadcastsLocked(p, appWidgetIds);
1579 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001581 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001582 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001583 if (id.host != null && id.host.callbacks != null) {
1584 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001585 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 } catch (RemoteException ex) {
1587 // It failed; remove the callback. No need to prune because
1588 // we know that this host is still referenced by this
1589 // instance.
1590 id.host.callbacks = null;
1591 }
1592 }
1593 }
1594 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001595 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 }
1597 }
1598 }
1599 }
1600 }
1601
1602 // prune the ones we don't want to keep
1603 N = mInstalledProviders.size();
1604 for (int i=N-1; i>=0; i--) {
1605 Provider p = mInstalledProviders.get(i);
1606 if (pkgName.equals(p.info.provider.getPackageName())
1607 && !keep.contains(p.info.provider.getClassName())) {
1608 removeProviderLocked(i, p);
1609 }
1610 }
1611 }
1612
1613 void removeProvidersForPackageLocked(String pkgName) {
1614 int N = mInstalledProviders.size();
1615 for (int i=N-1; i>=0; i--) {
1616 Provider p = mInstalledProviders.get(i);
1617 if (pkgName.equals(p.info.provider.getPackageName())) {
1618 removeProviderLocked(i, p);
1619 }
1620 }
1621
1622 // Delete the hosts for this package too
1623 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001624 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001625 // so we don't need to worry about sending DISABLE broadcasts to them.
1626 N = mHosts.size();
1627 for (int i=N-1; i>=0; i--) {
1628 Host host = mHosts.get(i);
1629 if (pkgName.equals(host.packageName)) {
1630 deleteHostLocked(host);
1631 }
1632 }
1633 }
1634}
1635