AppWidgetService.java revision 483f3b06ea84440a082e21b68ec2c2e54046f5a6
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.appwidget.AppWidgetManager;
22import android.appwidget.AppWidgetProviderInfo;
23import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.ServiceConnection;
29import android.content.pm.PackageManager;
30import android.os.Binder;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.util.Pair;
34import android.util.Slog;
35import android.util.SparseArray;
36import android.widget.RemoteViews;
37
38import com.android.internal.appwidget.IAppWidgetHost;
39import com.android.internal.appwidget.IAppWidgetService;
40import com.android.internal.widget.IRemoteViewsAdapterConnection;
41
42import java.io.FileDescriptor;
43import java.io.PrintWriter;
44import java.util.ArrayList;
45import java.util.List;
46import java.util.Locale;
47
48
49/**
50 * Redirects calls to this service to the instance of the service for the appropriate user.
51 */
52class AppWidgetService extends IAppWidgetService.Stub
53{
54    private static final String TAG = "AppWidgetService";
55
56    /*
57     * When identifying a Host or Provider based on the calling process, use the uid field.
58     * When identifying a Host or Provider based on a package manager broadcast, use the
59     * package given.
60     */
61
62    static class Provider {
63        int uid;
64        AppWidgetProviderInfo info;
65        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
66        PendingIntent broadcast;
67        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
68
69        int tag;    // for use while saving state (the index)
70    }
71
72    static class Host {
73        int uid;
74        int hostId;
75        String packageName;
76        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
77        IAppWidgetHost callbacks;
78        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
79
80        int tag;    // for use while saving state (the index)
81    }
82
83    static class AppWidgetId {
84        int appWidgetId;
85        Provider provider;
86        RemoteViews views;
87        Host host;
88    }
89
90    /**
91     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
92     * This needs to be a static inner class since a reference to the ServiceConnection is held
93     * globally and may lead us to leak AppWidgetService instances (if there were more than one).
94     */
95    static class ServiceConnectionProxy implements ServiceConnection {
96        private final IBinder mConnectionCb;
97
98        ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
99            mConnectionCb = connectionCb;
100        }
101        public void onServiceConnected(ComponentName name, IBinder service) {
102            final IRemoteViewsAdapterConnection cb =
103                IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
104            try {
105                cb.onServiceConnected(service);
106            } catch (Exception e) {
107                e.printStackTrace();
108            }
109        }
110        public void onServiceDisconnected(ComponentName name) {
111            disconnect();
112        }
113        public void disconnect() {
114            final IRemoteViewsAdapterConnection cb =
115                IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
116            try {
117                cb.onServiceDisconnected();
118            } catch (Exception e) {
119                e.printStackTrace();
120            }
121        }
122    }
123
124    Context mContext;
125    Locale mLocale;
126    PackageManager mPackageManager;
127    AlarmManager mAlarmManager;
128    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
129    int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
130    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
131    ArrayList<Host> mHosts = new ArrayList<Host>();
132    boolean mSafeMode;
133
134
135    private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
136
137    AppWidgetService(Context context) {
138        mContext = context;
139        mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
140        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0);
141        mAppWidgetServices.append(0, primary);
142    }
143
144    public void systemReady(boolean safeMode) {
145        mSafeMode = safeMode;
146
147        mAppWidgetServices.get(0).systemReady(safeMode);
148
149        // Register for the boot completed broadcast, so we can send the
150        // ENABLE broacasts. If we try to send them now, they time out,
151        // because the system isn't ready to handle them yet.
152        mContext.registerReceiver(mBroadcastReceiver,
153                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
154
155        // Register for configuration changes so we can update the names
156        // of the widgets when the locale changes.
157        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
158                Intent.ACTION_CONFIGURATION_CHANGED), null, null);
159
160        // Register for broadcasts about package install, etc., so we can
161        // update the provider list.
162        IntentFilter filter = new IntentFilter();
163        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
164        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
165        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
166        filter.addDataScheme("package");
167        mContext.registerReceiver(mBroadcastReceiver, filter);
168        // Register for events related to sdcard installation.
169        IntentFilter sdFilter = new IntentFilter();
170        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
171        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
172        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
173    }
174
175    @Override
176    public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
177        return getImplForUser().allocateAppWidgetId(packageName, hostId);
178    }
179
180    @Override
181    public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
182        getImplForUser().deleteAppWidgetId(appWidgetId);
183    }
184
185    @Override
186    public void deleteHost(int hostId) throws RemoteException {
187        getImplForUser().deleteHost(hostId);
188    }
189
190    @Override
191    public void deleteAllHosts() throws RemoteException {
192        getImplForUser().deleteAllHosts();
193    }
194
195    void cancelBroadcasts(Provider p) {
196        if (p.broadcast != null) {
197            mAlarmManager.cancel(p.broadcast);
198            long token = Binder.clearCallingIdentity();
199            try {
200                p.broadcast.cancel();
201            } finally {
202                Binder.restoreCallingIdentity(token);
203            }
204            p.broadcast = null;
205        }
206    }
207
208    @Override
209    public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
210        getImplForUser().bindAppWidgetId(appWidgetId, provider);
211    }
212
213    @Override
214    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
215            throws RemoteException {
216        getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection);
217    }
218
219    @Override
220    public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
221            List<RemoteViews> updatedViews) throws RemoteException {
222        return getImplForUser().startListening(host, packageName, hostId, updatedViews);
223    }
224
225    // TODO: Call this from PackageManagerService when a user is removed
226    public void removeUser(int userId) {
227    }
228
229    private AppWidgetServiceImpl getImplForUser() {
230        final int userId = Binder.getOrigCallingUser();
231        AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
232        if (service == null) {
233            Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
234            // TODO: Verify that it's a valid user
235            service = new AppWidgetServiceImpl(mContext, userId);
236            service.systemReady(mSafeMode);
237            // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
238            service.sendInitialBroadcasts();
239            mAppWidgetServices.append(userId, service);
240        }
241
242        return service;
243    }
244
245    @Override
246    public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
247        return getImplForUser().getAppWidgetIds(provider);
248    }
249
250    @Override
251    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
252        return getImplForUser().getAppWidgetInfo(appWidgetId);
253    }
254
255    @Override
256    public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
257        return getImplForUser().getAppWidgetViews(appWidgetId);
258    }
259
260    static int[] getAppWidgetIds(Provider p) {
261        int instancesSize = p.instances.size();
262        int appWidgetIds[] = new int[instancesSize];
263        for (int i=0; i<instancesSize; i++) {
264            appWidgetIds[i] = p.instances.get(i).appWidgetId;
265        }
266        return appWidgetIds;
267    }
268
269    @Override
270    public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
271        return getImplForUser().getInstalledProviders();
272    }
273
274    @Override
275    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
276            throws RemoteException {
277        getImplForUser().notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
278    }
279
280    @Override
281    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
282            throws RemoteException {
283        getImplForUser().partiallyUpdateAppWidgetIds(appWidgetIds, views);
284    }
285
286    @Override
287    public void stopListening(int hostId) throws RemoteException {
288        getImplForUser().stopListening(hostId);
289    }
290
291    @Override
292    public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
293        getImplForUser().unbindRemoteViewsService(appWidgetId, intent);
294    }
295
296    @Override
297    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
298        getImplForUser().updateAppWidgetIds(appWidgetIds, views);
299    }
300
301    @Override
302    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
303            throws RemoteException {
304        getImplForUser().updateAppWidgetProvider(provider, views);
305    }
306
307    @Override
308    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
309        // Dump the state of all the app widget providers
310        for (int i = 0; i < mAppWidgetServices.size(); i++) {
311            AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
312            service.dump(fd, pw, args);
313        }
314    }
315
316    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
317        public void onReceive(Context context, Intent intent) {
318            String action = intent.getAction();
319            // Slog.d(TAG, "received " + action);
320            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
321                getImplForUser().sendInitialBroadcasts();
322            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
323                for (int i = 0; i < mAppWidgetServices.size(); i++) {
324                    AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
325                    service.onConfigurationChanged();
326                }
327            } else {
328                for (int i = 0; i < mAppWidgetServices.size(); i++) {
329                    AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
330                    service.onBroadcastReceived(intent);
331                }
332            }
333        }
334    };
335}
336