1 /*
2  * Copyright (C) 2018 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.NonNull;
20 import android.annotation.Nullable;
21 import android.media.AudioAttributes;
22 import android.util.Slog;
23 
24 import com.android.internal.util.Preconditions;
25 
26 import java.util.NoSuchElementException;
27 
28 /**
29  * An ExternalVibration represents an on-going vibration being controlled by something other than
30  * the core vibrator service.
31  *
32  * @hide
33  */
34 public class ExternalVibration implements Parcelable {
35     private static final String TAG = "ExternalVibration";
36     private int mUid;
37     @NonNull
38     private String mPkg;
39     @NonNull
40     private AudioAttributes mAttrs;
41     @NonNull
42     private IExternalVibrationController mController;
43     // A token used to maintain equality comparisons when passing objects across process
44     // boundaries.
45     @NonNull
46     private IBinder mToken;
ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs, @NonNull IExternalVibrationController controller)47     public ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
48             @NonNull IExternalVibrationController controller) {
49         this(uid, pkg, attrs, controller, new Binder());
50     }
51 
52     /**
53      * Full constructor, but exposed to construct the ExternalVibration with an explicit binder
54      * token (for mocks).
55      *
56      * @hide
57      */
ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs, @NonNull IExternalVibrationController controller, @NonNull IBinder token)58     public ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
59             @NonNull IExternalVibrationController controller, @NonNull IBinder token) {
60         mUid = uid;
61         mPkg = Preconditions.checkNotNull(pkg);
62         mAttrs = Preconditions.checkNotNull(attrs);
63         mController = Preconditions.checkNotNull(controller);
64         mToken = Preconditions.checkNotNull(token);
65 
66         // IExternalVibrationController is a hidden AIDL interface with implementation provided by
67         // the audio framework to allow mute/unmute control over the external vibration.
68         //
69         // Transactions are locked in audioflinger, and should be blocking to avoid racing
70         // conditions on multiple audio playback.
71         //
72         // They can also be triggered before starting a new external vibration in
73         // IExternalVibratorService, as the ongoing external vibration needs to be muted before the
74         // new one can start, which also requires blocking calls to mute.
75         Binder.allowBlocking(mController.asBinder());
76     }
77 
ExternalVibration(Parcel in)78     private ExternalVibration(Parcel in) {
79         this(in.readInt(), in.readString(), readAudioAttributes(in),
80                 IExternalVibrationController.Stub.asInterface(in.readStrongBinder()),
81                 in.readStrongBinder());
82     }
83 
readAudioAttributes(Parcel in)84     private static AudioAttributes readAudioAttributes(Parcel in) {
85         int usage = in.readInt();
86         int contentType = in.readInt();
87         int capturePreset = in.readInt();
88         int flags = in.readInt();
89         AudioAttributes.Builder builder = new AudioAttributes.Builder();
90         return builder.setUsage(usage)
91                 .setContentType(contentType)
92                 .setCapturePreset(capturePreset)
93                 .setFlags(flags)
94                 .build();
95     }
96 
getUid()97     public int getUid() {
98         return mUid;
99     }
100 
getPackage()101     public String getPackage() {
102         return mPkg;
103     }
104 
getAudioAttributes()105     public AudioAttributes getAudioAttributes() {
106         return mAttrs;
107     }
108 
getToken()109     public IBinder getToken() {
110         return mToken;
111     }
112 
getVibrationAttributes()113     public VibrationAttributes getVibrationAttributes() {
114         return new VibrationAttributes.Builder(mAttrs).build();
115     }
116 
117     /**
118      * Mutes the external vibration if it's playing and unmuted.
119      *
120      * @return whether the muting operation was successful
121      */
mute()122     public boolean mute() {
123         try {
124             mController.mute();
125         } catch (RemoteException e) {
126             Slog.wtf(TAG, "Failed to mute vibration stream: " + this, e);
127             return false;
128         }
129         return true;
130     }
131 
132     /**
133      * Unmutes the external vibration if it's playing and muted.
134      *
135      * @return whether the unmuting operation was successful
136      */
unmute()137     public boolean unmute() {
138         try {
139             mController.unmute();
140         } catch (RemoteException e) {
141             Slog.wtf(TAG, "Failed to unmute vibration stream: " + this, e);
142             return false;
143         }
144         return true;
145     }
146 
147     /**
148      * Links a recipient to death against this external vibration token
149      */
linkToDeath(IBinder.DeathRecipient recipient)150     public void linkToDeath(IBinder.DeathRecipient recipient) {
151         try {
152             mToken.linkToDeath(recipient, 0);
153         } catch (RemoteException e) {
154             Slog.wtf(TAG, "Failed to link to token death: " + this, e);
155         }
156     }
157 
158     /**
159      * Unlinks a recipient to death against this external vibration token
160      */
unlinkToDeath(IBinder.DeathRecipient recipient)161     public void unlinkToDeath(IBinder.DeathRecipient recipient) {
162         try {
163             mToken.unlinkToDeath(recipient, 0);
164         } catch (NoSuchElementException e) {
165             Slog.wtf(TAG, "Failed to unlink to token death", e);
166         }
167     }
168 
169     @Override
equals(@ullable Object o)170     public boolean equals(@Nullable Object o) {
171         if (o == null || !(o instanceof ExternalVibration)) {
172             return false;
173         }
174         ExternalVibration other = (ExternalVibration) o;
175         return mToken.equals(other.mToken);
176     }
177 
178     @Override
toString()179     public String toString() {
180         return "ExternalVibration{"
181             + "uid=" + mUid + ", "
182             + "pkg=" + mPkg + ", "
183             + "attrs=" + mAttrs + ", "
184             + "controller=" + mController
185             + "token=" + mToken
186             + "}";
187     }
188 
189     @Override
writeToParcel(Parcel out, int flags)190     public void writeToParcel(Parcel out, int flags) {
191         out.writeInt(mUid);
192         out.writeString(mPkg);
193         writeAudioAttributes(mAttrs, out);
194         out.writeStrongBinder(mController.asBinder());
195         out.writeStrongBinder(mToken);
196     }
197 
writeAudioAttributes(AudioAttributes attrs, Parcel out)198     private static void writeAudioAttributes(AudioAttributes attrs, Parcel out) {
199         out.writeInt(attrs.getUsage());
200         out.writeInt(attrs.getContentType());
201         out.writeInt(attrs.getCapturePreset());
202         out.writeInt(attrs.getAllFlags());
203     }
204 
205     @Override
describeContents()206     public int describeContents() {
207         return 0;
208     }
209 
210     public static final @android.annotation.NonNull Parcelable.Creator<ExternalVibration> CREATOR =
211             new Parcelable.Creator<ExternalVibration>() {
212                 @Override
213                 public ExternalVibration createFromParcel(Parcel in) {
214                     return new ExternalVibration(in);
215                 }
216 
217                 @Override
218                 public ExternalVibration[] newArray(int size) {
219                     return new ExternalVibration[size];
220                 }
221             };
222 }
223