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.ActivityManagerNative;
20import android.appwidget.AppWidgetProviderInfo;
21import android.content.BroadcastReceiver;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.HandlerThread;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.os.UserHandle;
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.util.IndentingPrintWriter;
41
42import java.io.FileDescriptor;
43import java.io.PrintWriter;
44import java.util.List;
45import java.util.Locale;
46
47
48/**
49 * Redirects calls to this service to the instance of the service for the appropriate user.
50 */
51class AppWidgetService extends IAppWidgetService.Stub
52{
53    private static final String TAG = "AppWidgetService";
54
55    Context mContext;
56    Locale mLocale;
57    PackageManager mPackageManager;
58    boolean mSafeMode;
59    private final Handler mSaveStateHandler;
60
61    private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
62
63    AppWidgetService(Context context) {
64        mContext = context;
65
66        HandlerThread handlerThread = new HandlerThread("AppWidgetService -- Save state");
67        handlerThread.start();
68        mSaveStateHandler = new Handler(handlerThread.getLooper());
69
70        mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
71        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0, mSaveStateHandler);
72        mAppWidgetServices.append(0, primary);
73    }
74
75    public void systemReady(boolean safeMode) {
76        mSafeMode = safeMode;
77
78        mAppWidgetServices.get(0).systemReady(safeMode);
79
80        // Register for the boot completed broadcast, so we can send the
81        // ENABLE broacasts. If we try to send them now, they time out,
82        // because the system isn't ready to handle them yet.
83        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
84                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
85
86        // Register for configuration changes so we can update the names
87        // of the widgets when the locale changes.
88        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
89                new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
90
91        // Register for broadcasts about package install, etc., so we can
92        // update the provider list.
93        IntentFilter filter = new IntentFilter();
94        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
95        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
96        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
97        filter.addDataScheme("package");
98        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
99                filter, null, null);
100        // Register for events related to sdcard installation.
101        IntentFilter sdFilter = new IntentFilter();
102        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
103        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
104        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
105                sdFilter, null, null);
106
107        IntentFilter userFilter = new IntentFilter();
108        userFilter.addAction(Intent.ACTION_USER_REMOVED);
109        userFilter.addAction(Intent.ACTION_USER_STOPPING);
110        mContext.registerReceiver(new BroadcastReceiver() {
111            @Override
112            public void onReceive(Context context, Intent intent) {
113                if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
114                    onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
115                            UserHandle.USER_NULL));
116                } else if (Intent.ACTION_USER_STOPPING.equals(intent.getAction())) {
117                    onUserStopping(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
118                            UserHandle.USER_NULL));
119                }
120            }
121        }, userFilter);
122    }
123
124    /**
125     * This returns the user id of the caller, if the caller is not the system process,
126     * otherwise it assumes that the calls are from the lockscreen and hence are meant for the
127     * current user. TODO: Instead, have lockscreen make explicit calls with userId
128     */
129    private int getCallingOrCurrentUserId() {
130        int callingUid = Binder.getCallingUid();
131        // Also check the PID because Settings (power control widget) also runs as System UID
132        if (callingUid == android.os.Process.myUid()
133                && Binder.getCallingPid() == android.os.Process.myPid()) {
134            try {
135                return ActivityManagerNative.getDefault().getCurrentUser().id;
136            } catch (RemoteException re) {
137                return UserHandle.getUserId(callingUid);
138            }
139        } else {
140            return UserHandle.getUserId(callingUid);
141        }
142    }
143
144    @Override
145    public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
146        return getImplForUser(getCallingOrCurrentUserId()).allocateAppWidgetId(
147                packageName, hostId);
148    }
149
150    @Override
151    public int[] getAppWidgetIdsForHost(int hostId) throws RemoteException {
152        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetIdsForHost(hostId);
153    }
154
155    @Override
156    public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
157        getImplForUser(getCallingOrCurrentUserId()).deleteAppWidgetId(appWidgetId);
158    }
159
160    @Override
161    public void deleteHost(int hostId) throws RemoteException {
162        getImplForUser(getCallingOrCurrentUserId()).deleteHost(hostId);
163    }
164
165    @Override
166    public void deleteAllHosts() throws RemoteException {
167        getImplForUser(getCallingOrCurrentUserId()).deleteAllHosts();
168    }
169
170    @Override
171    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options)
172            throws RemoteException {
173        getImplForUser(getCallingOrCurrentUserId()).bindAppWidgetId(appWidgetId, provider,
174                options);
175    }
176
177    @Override
178    public boolean bindAppWidgetIdIfAllowed(
179            String packageName, int appWidgetId, ComponentName provider, Bundle options)
180                    throws RemoteException {
181        return getImplForUser(getCallingOrCurrentUserId()).bindAppWidgetIdIfAllowed(
182                packageName, appWidgetId, provider, options);
183    }
184
185    @Override
186    public boolean hasBindAppWidgetPermission(String packageName) throws RemoteException {
187        return getImplForUser(getCallingOrCurrentUserId()).hasBindAppWidgetPermission(
188                packageName);
189    }
190
191    @Override
192    public void setBindAppWidgetPermission(String packageName, boolean permission)
193            throws RemoteException {
194        getImplForUser(getCallingOrCurrentUserId()).setBindAppWidgetPermission(
195                packageName, permission);
196    }
197
198    @Override
199    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection,
200            int userId) throws RemoteException {
201        if (Binder.getCallingPid() != android.os.Process.myPid()
202                && userId != UserHandle.getCallingUserId()) {
203            throw new SecurityException("Call from non-system process. Calling uid = "
204                    + Binder.getCallingUid());
205        }
206        getImplForUser(userId).bindRemoteViewsService(
207                appWidgetId, intent, connection);
208    }
209
210    @Override
211    public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
212            List<RemoteViews> updatedViews) throws RemoteException {
213        return getImplForUser(getCallingOrCurrentUserId()).startListening(host,
214                packageName, hostId, updatedViews);
215    }
216
217    @Override
218    public int[] startListeningAsUser(IAppWidgetHost host, String packageName, int hostId,
219            List<RemoteViews> updatedViews, int userId) throws RemoteException {
220        if (Binder.getCallingPid() != android.os.Process.myPid()
221                && userId != UserHandle.getCallingUserId()) {
222            throw new SecurityException("Call from non-system process. Calling uid = "
223                    + Binder.getCallingUid());
224        }
225        return getImplForUser(userId).startListening(host, packageName, hostId, updatedViews);
226    }
227
228    public void onUserRemoved(int userId) {
229        if (userId < 1) return;
230        synchronized (mAppWidgetServices) {
231            AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
232            mAppWidgetServices.remove(userId);
233
234            if (impl == null) {
235                AppWidgetServiceImpl.getSettingsFile(userId).delete();
236            } else {
237                impl.onUserRemoved();
238            }
239        }
240    }
241
242    public void onUserStopping(int userId) {
243        if (userId < 1) return;
244        synchronized (mAppWidgetServices) {
245            AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
246            if (impl != null) {
247                mAppWidgetServices.remove(userId);
248                impl.onUserStopping();
249            }
250        }
251    }
252
253    private AppWidgetServiceImpl getImplForUser(int userId) {
254        boolean sendInitial = false;
255        AppWidgetServiceImpl service;
256        synchronized (mAppWidgetServices) {
257            service = mAppWidgetServices.get(userId);
258            if (service == null) {
259                Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId + ", adding");
260                // TODO: Verify that it's a valid user
261                service = new AppWidgetServiceImpl(mContext, userId, mSaveStateHandler);
262                service.systemReady(mSafeMode);
263                // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
264                mAppWidgetServices.append(userId, service);
265                sendInitial = true;
266            }
267        }
268        if (sendInitial) {
269            service.sendInitialBroadcasts();
270        }
271        return service;
272    }
273
274    @Override
275    public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
276        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetIds(provider);
277    }
278
279    @Override
280    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
281        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetInfo(appWidgetId);
282    }
283
284    @Override
285    public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
286        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetViews(appWidgetId);
287    }
288
289    @Override
290    public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
291        getImplForUser(getCallingOrCurrentUserId()).updateAppWidgetOptions(appWidgetId, options);
292    }
293
294    @Override
295    public Bundle getAppWidgetOptions(int appWidgetId) {
296        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetOptions(appWidgetId);
297    }
298
299    @Override
300    public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter)
301            throws RemoteException {
302        return getImplForUser(getCallingOrCurrentUserId()).getInstalledProviders(categoryFilter);
303    }
304
305    @Override
306    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
307            throws RemoteException {
308        getImplForUser(getCallingOrCurrentUserId()).notifyAppWidgetViewDataChanged(
309                appWidgetIds, viewId);
310    }
311
312    @Override
313    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
314            throws RemoteException {
315        getImplForUser(getCallingOrCurrentUserId()).partiallyUpdateAppWidgetIds(
316                appWidgetIds, views);
317    }
318
319    @Override
320    public void stopListening(int hostId) throws RemoteException {
321        getImplForUser(getCallingOrCurrentUserId()).stopListening(hostId);
322    }
323
324    @Override
325    public void stopListeningAsUser(int hostId, int userId) throws RemoteException {
326        if (Binder.getCallingPid() != android.os.Process.myPid()
327                && userId != UserHandle.getCallingUserId()) {
328            throw new SecurityException("Call from non-system process. Calling uid = "
329                    + Binder.getCallingUid());
330        }
331        getImplForUser(userId).stopListening(hostId);
332    }
333
334    @Override
335    public void unbindRemoteViewsService(int appWidgetId, Intent intent, int userId)
336            throws RemoteException {
337        if (Binder.getCallingPid() != android.os.Process.myPid()
338                && userId != UserHandle.getCallingUserId()) {
339            throw new SecurityException("Call from non-system process. Calling uid = "
340                    + Binder.getCallingUid());
341        }
342        getImplForUser(userId).unbindRemoteViewsService(
343                appWidgetId, intent);
344    }
345
346    @Override
347    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
348        getImplForUser(getCallingOrCurrentUserId()).updateAppWidgetIds(appWidgetIds, views);
349    }
350
351    @Override
352    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
353            throws RemoteException {
354        getImplForUser(getCallingOrCurrentUserId()).updateAppWidgetProvider(provider, views);
355    }
356
357    @Override
358    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
359        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
360
361        // Dump the state of all the app widget providers
362        synchronized (mAppWidgetServices) {
363            IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
364            for (int i = 0; i < mAppWidgetServices.size(); i++) {
365                pw.println("User: " + mAppWidgetServices.keyAt(i));
366                ipw.increaseIndent();
367                AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
368                service.dump(fd, ipw, args);
369                ipw.decreaseIndent();
370            }
371        }
372    }
373
374    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
375        public void onReceive(Context context, Intent intent) {
376            String action = intent.getAction();
377            // Slog.d(TAG, "received " + action);
378            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
379                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
380                if (userId >= 0) {
381                    getImplForUser(userId).sendInitialBroadcasts();
382                } else {
383                    Slog.w(TAG, "Incorrect user handle supplied in " + intent);
384                }
385            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
386                for (int i = 0; i < mAppWidgetServices.size(); i++) {
387                    AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
388                    service.onConfigurationChanged();
389                }
390            } else {
391                int sendingUser = getSendingUserId();
392                if (sendingUser == UserHandle.USER_ALL) {
393                    for (int i = 0; i < mAppWidgetServices.size(); i++) {
394                        AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
395                        service.onBroadcastReceived(intent);
396                    }
397                } else {
398                    AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser);
399                    if (service != null) {
400                        service.onBroadcastReceived(intent);
401                    }
402                }
403            }
404        }
405    };
406}
407