1/*
2 * Copyright (C) 2012 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.content.Context;
20import android.content.Intent;
21import android.content.pm.PackageManager;
22import android.os.Binder;
23import android.os.Handler;
24import android.os.IBinder;
25import android.os.IUpdateLock;
26import android.os.RemoteException;
27import android.os.TokenWatcher;
28import android.os.UpdateLock;
29import android.os.UserHandle;
30import android.util.Slog;
31
32import com.android.internal.util.DumpUtils;
33
34import java.io.FileDescriptor;
35import java.io.PrintWriter;
36
37public class UpdateLockService extends IUpdateLock.Stub {
38    static final boolean DEBUG = false;
39    static final String TAG = "UpdateLockService";
40
41    // signatureOrSystem required to use update locks
42    static final String PERMISSION = "android.permission.UPDATE_LOCK";
43
44    Context mContext;
45    LockWatcher mLocks;
46
47    class LockWatcher extends TokenWatcher {
48        LockWatcher(Handler h, String tag) {
49            super(h, tag);
50        }
51
52        public void acquired() {
53            if (DEBUG) {
54                Slog.d(TAG, "first acquire; broadcasting convenient=false");
55            }
56            sendLockChangedBroadcast(false);
57        }
58        public void released() {
59            if (DEBUG) {
60                Slog.d(TAG, "last release; broadcasting convenient=true");
61            }
62            sendLockChangedBroadcast(true);
63        }
64    }
65
66    UpdateLockService(Context context) {
67        mContext = context;
68        mLocks = new LockWatcher(new Handler(), "UpdateLocks");
69
70        // Consider just-booting to be a reasonable time to allow
71        // interruptions for update installation etc.
72        sendLockChangedBroadcast(true);
73    }
74
75    void sendLockChangedBroadcast(boolean state) {
76        // Safe early during boot because this broadcast only goes to registered receivers.
77        long oldIdent = Binder.clearCallingIdentity();
78        try {
79            Intent intent = new Intent(UpdateLock.UPDATE_LOCK_CHANGED)
80                    .putExtra(UpdateLock.NOW_IS_CONVENIENT, state)
81                    .putExtra(UpdateLock.TIMESTAMP, System.currentTimeMillis())
82                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
83            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
84        } finally {
85            Binder.restoreCallingIdentity(oldIdent);
86        }
87    }
88
89    @Override
90    public void acquireUpdateLock(IBinder token, String tag) throws RemoteException {
91        if (DEBUG) {
92            Slog.d(TAG, "acquire(" + token + ") by " + makeTag(tag));
93        }
94
95        mContext.enforceCallingOrSelfPermission(PERMISSION, "acquireUpdateLock");
96        mLocks.acquire(token, makeTag(tag));
97    }
98
99    @Override
100    public void releaseUpdateLock(IBinder token) throws RemoteException {
101        if (DEBUG) {
102            Slog.d(TAG, "release(" + token + ')');
103        }
104
105        mContext.enforceCallingOrSelfPermission(PERMISSION, "releaseUpdateLock");
106        mLocks.release(token);
107    };
108
109    private String makeTag(String tag) {
110        return "{tag=" + tag
111                + " uid=" + Binder.getCallingUid()
112                + " pid=" + Binder.getCallingPid() + '}';
113    }
114
115    @Override
116    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
117        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
118        mLocks.dump(pw);
119    }
120}
121