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