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