18662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate/*
28662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * Copyright (C) 2012 The Android Open Source Project
38662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate *
48662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * Licensed under the Apache License, Version 2.0 (the "License");
58662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * you may not use this file except in compliance with the License.
68662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * You may obtain a copy of the License at
78662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate *
88662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate *      http://www.apache.org/licenses/LICENSE-2.0
98662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate *
108662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * Unless required by applicable law or agreed to in writing, software
118662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * distributed under the License is distributed on an "AS IS" BASIS,
128662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * See the License for the specific language governing permissions and
148662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * limitations under the License.
158662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate */
168662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
178662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tatepackage android.os;
188662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
198662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tateimport android.content.Context;
208662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tateimport android.util.Log;
218662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
228662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate/**
238662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * Advisory wakelock-like mechanism by which processes that should not be interrupted for
248662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * OTA/update purposes can so advise the OS.  This is particularly relevant for headless
258662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * or kiosk-like operation.
268662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate *
278662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate * @hide
288662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate */
298662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tatepublic class UpdateLock {
308662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    private static final boolean DEBUG = false;
318662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    private static final String TAG = "UpdateLock";
328662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
338662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    private static IUpdateLock sService;
348662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    private static void checkService() {
358662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        if (sService == null) {
368662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            sService = IUpdateLock.Stub.asInterface(
378662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                    ServiceManager.getService(Context.UPDATE_LOCK_SERVICE));
388662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
398662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
408662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
418662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    IBinder mToken;
428662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    int mCount = 0;
438662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    boolean mRefCounted = true;
448662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    boolean mHeld = false;
458662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    final String mTag;
468662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
478662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    /**
488662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * Broadcast Intent action sent when the global update lock state changes,
498662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * i.e. when the first locker acquires an update lock, or when the last
508662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * locker releases theirs.  The broadcast is sticky but is sent only to
518662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * registered receivers.
528662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     */
538662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    public static final String UPDATE_LOCK_CHANGED = "android.os.UpdateLock.UPDATE_LOCK_CHANGED";
548662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
558662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    /**
568662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * Boolean Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, indicating
578662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * whether now is an appropriate time to interrupt device activity with an
588662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * update operation.  True means that updates are okay right now; false indicates
598662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * that perhaps later would be a better time.
608662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     */
618662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    public static final String NOW_IS_CONVENIENT = "nowisconvenient";
628662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
638662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    /**
648662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * Long Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, marking the
658662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * wall-clock time [in UTC] at which the broadcast was sent.  Note that this is
668662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * in the System.currentTimeMillis() time base, which may be non-monotonic especially
678662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * around reboots.
688662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     */
698662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    public static final String TIMESTAMP = "timestamp";
708662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
718662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    /**
728662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * Construct an UpdateLock instance.
738662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * @param tag An arbitrary string used to identify this lock instance in dump output.
748662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     */
758662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    public UpdateLock(String tag) {
768662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        mTag = tag;
778662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        mToken = new Binder();
788662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
798662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
808662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    /**
818662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * Change the refcount behavior of this update lock.
828662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     */
838662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    public void setReferenceCounted(boolean isRefCounted) {
848662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        if (DEBUG) {
858662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            Log.v(TAG, "setting refcounted=" + isRefCounted + " : " + this);
868662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
878662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        mRefCounted = isRefCounted;
888662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
898662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
908662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    /**
918662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * Is this lock currently held?
928662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     */
938662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    public boolean isHeld() {
948662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        synchronized (mToken) {
958662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            return mHeld;
968662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
978662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
988662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
998662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    /**
1008662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * Acquire an update lock.
1018662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     */
1028662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    public void acquire() {
1038662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        if (DEBUG) {
1048662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            Log.v(TAG, "acquire() : " + this, new RuntimeException("here"));
1058662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
1068662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        checkService();
1078662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        synchronized (mToken) {
1088662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            acquireLocked();
1098662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
1108662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
1118662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
1128662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    private void acquireLocked() {
1138662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        if (!mRefCounted || mCount++ == 0) {
1148662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            if (sService != null) {
1158662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                try {
1168662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                    sService.acquireUpdateLock(mToken, mTag);
1178662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                } catch (RemoteException e) {
1188662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                    Log.e(TAG, "Unable to contact service to acquire");
1198662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                }
1208662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            }
1218662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            mHeld = true;
1228662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
1238662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
1248662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
1258662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    /**
1268662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     * Release this update lock.
1278662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate     */
1288662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    public void release() {
1298662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        if (DEBUG) Log.v(TAG, "release() : " + this, new RuntimeException("here"));
1308662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        checkService();
1318662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        synchronized (mToken) {
1328662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            releaseLocked();
1338662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
1348662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
1358662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
1368662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    private void releaseLocked() {
1378662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        if (!mRefCounted || --mCount == 0) {
1388662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            if (sService != null) {
1398662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                try {
1408662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                    sService.releaseUpdateLock(mToken);
1418662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                } catch (RemoteException e) {
1428662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                    Log.e(TAG, "Unable to contact service to release");
1438662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                }
1448662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            }
1458662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            mHeld = false;
1468662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
1478662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        if (mCount < 0) {
1488662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            throw new RuntimeException("UpdateLock under-locked");
1498662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
1508662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
1518662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate
1528662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    @Override
1538662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    protected void finalize() throws Throwable {
1548662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        synchronized (mToken) {
1558662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            // if mHeld is true, sService must be non-null
1568662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            if (mHeld) {
1578662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                Log.wtf(TAG, "UpdateLock finalized while still held");
1588662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                try {
1598662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                    sService.releaseUpdateLock(mToken);
1608662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                } catch (RemoteException e) {
1618662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                    Log.e(TAG, "Unable to contact service to release");
1628662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate                }
1638662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate            }
1648662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate        }
1658662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate    }
1668662cab5c6a01ea5c426512e6f6d2cf3e158aea0Christopher Tate}
167