blob: 036d0951c19af05c1377798b870fc22d2b2b94c8 [file] [log] [blame]
Christopher Tate8662cab52012-02-23 14:59:36 -08001/*
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 android.os;
18
Artur Satayevafdb23a2019-12-10 17:47:53 +000019import android.compat.annotation.UnsupportedAppUsage;
Christopher Tate8662cab52012-02-23 14:59:36 -080020import android.content.Context;
21import 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 */
30public 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 */
Andrei Onea24ec3212019-03-15 17:35:05 +000054 @UnsupportedAppUsage
Christopher Tate8662cab52012-02-23 14:59:36 -080055 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 */
Andrei Onea24ec3212019-03-15 17:35:05 +000063 @UnsupportedAppUsage
Christopher Tate8662cab52012-02-23 14:59:36 -080064 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 */
Andrei Onea24ec3212019-03-15 17:35:05 +000072 @UnsupportedAppUsage
Christopher Tate8662cab52012-02-23 14:59:36 -080073 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 */
Andrei Onea24ec3212019-03-15 17:35:05 +000097 @UnsupportedAppUsage
Christopher Tate8662cab52012-02-23 14:59:36 -080098 public boolean isHeld() {
99 synchronized (mToken) {
100 return mHeld;
101 }
102 }
103
104 /**
105 * Acquire an update lock.
106 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000107 @UnsupportedAppUsage
Christopher Tate8662cab52012-02-23 14:59:36 -0800108 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 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000134 @UnsupportedAppUsage
Christopher Tate8662cab52012-02-23 14:59:36 -0800135 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}