Home | History | Annotate | Download | only in os
      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 
     17 package android.os;
     18 
     19 import android.annotation.UnsupportedAppUsage;
     20 import android.content.Context;
     21 import android.util.Log;
     22 
     23 /**
     24  * Advisory wakelock-like mechanism by which processes that should not be interrupted for
     25  * OTA/update purposes can so advise the OS.  This is particularly relevant for headless
     26  * or kiosk-like operation.
     27  *
     28  * @hide
     29  */
     30 public class UpdateLock {
     31     private static final boolean DEBUG = false;
     32     private static final String TAG = "UpdateLock";
     33 
     34     private static IUpdateLock sService;
     35     private static void checkService() {
     36         if (sService == null) {
     37             sService = IUpdateLock.Stub.asInterface(
     38                     ServiceManager.getService(Context.UPDATE_LOCK_SERVICE));
     39         }
     40     }
     41 
     42     IBinder mToken;
     43     int mCount = 0;
     44     boolean mRefCounted = true;
     45     boolean mHeld = false;
     46     final String mTag;
     47 
     48     /**
     49      * Broadcast Intent action sent when the global update lock state changes,
     50      * i.e. when the first locker acquires an update lock, or when the last
     51      * locker releases theirs.  The broadcast is sticky but is sent only to
     52      * registered receivers.
     53      */
     54     @UnsupportedAppUsage
     55     public static final String UPDATE_LOCK_CHANGED = "android.os.UpdateLock.UPDATE_LOCK_CHANGED";
     56 
     57     /**
     58      * Boolean Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, indicating
     59      * whether now is an appropriate time to interrupt device activity with an
     60      * update operation.  True means that updates are okay right now; false indicates
     61      * that perhaps later would be a better time.
     62      */
     63     @UnsupportedAppUsage
     64     public static final String NOW_IS_CONVENIENT = "nowisconvenient";
     65 
     66     /**
     67      * Long Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, marking the
     68      * wall-clock time [in UTC] at which the broadcast was sent.  Note that this is
     69      * in the System.currentTimeMillis() time base, which may be non-monotonic especially
     70      * around reboots.
     71      */
     72     @UnsupportedAppUsage
     73     public static final String TIMESTAMP = "timestamp";
     74 
     75     /**
     76      * Construct an UpdateLock instance.
     77      * @param tag An arbitrary string used to identify this lock instance in dump output.
     78      */
     79     public UpdateLock(String tag) {
     80         mTag = tag;
     81         mToken = new Binder();
     82     }
     83 
     84     /**
     85      * Change the refcount behavior of this update lock.
     86      */
     87     public void setReferenceCounted(boolean isRefCounted) {
     88         if (DEBUG) {
     89             Log.v(TAG, "setting refcounted=" + isRefCounted + " : " + this);
     90         }
     91         mRefCounted = isRefCounted;
     92     }
     93 
     94     /**
     95      * Is this lock currently held?
     96      */
     97     @UnsupportedAppUsage
     98     public boolean isHeld() {
     99         synchronized (mToken) {
    100             return mHeld;
    101         }
    102     }
    103 
    104     /**
    105      * Acquire an update lock.
    106      */
    107     @UnsupportedAppUsage
    108     public void acquire() {
    109         if (DEBUG) {
    110             Log.v(TAG, "acquire() : " + this, new RuntimeException("here"));
    111         }
    112         checkService();
    113         synchronized (mToken) {
    114             acquireLocked();
    115         }
    116     }
    117 
    118     private void acquireLocked() {
    119         if (!mRefCounted || mCount++ == 0) {
    120             if (sService != null) {
    121                 try {
    122                     sService.acquireUpdateLock(mToken, mTag);
    123                 } catch (RemoteException e) {
    124                     Log.e(TAG, "Unable to contact service to acquire");
    125                 }
    126             }
    127             mHeld = true;
    128         }
    129     }
    130 
    131     /**
    132      * Release this update lock.
    133      */
    134     @UnsupportedAppUsage
    135     public void release() {
    136         if (DEBUG) Log.v(TAG, "release() : " + this, new RuntimeException("here"));
    137         checkService();
    138         synchronized (mToken) {
    139             releaseLocked();
    140         }
    141     }
    142 
    143     private void releaseLocked() {
    144         if (!mRefCounted || --mCount == 0) {
    145             if (sService != null) {
    146                 try {
    147                     sService.releaseUpdateLock(mToken);
    148                 } catch (RemoteException e) {
    149                     Log.e(TAG, "Unable to contact service to release");
    150                 }
    151             }
    152             mHeld = false;
    153         }
    154         if (mCount < 0) {
    155             throw new RuntimeException("UpdateLock under-locked");
    156         }
    157     }
    158 
    159     @Override
    160     protected void finalize() throws Throwable {
    161         synchronized (mToken) {
    162             // if mHeld is true, sService must be non-null
    163             if (mHeld) {
    164                 Log.wtf(TAG, "UpdateLock finalized while still held");
    165                 try {
    166                     sService.releaseUpdateLock(mToken);
    167                 } catch (RemoteException e) {
    168                     Log.e(TAG, "Unable to contact service to release");
    169                 }
    170             }
    171         }
    172     }
    173 }
    174