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