1 /*
2  * Copyright (C) 2019 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 #include "vibrator-impl/Vibrator.h"
18 
19 #include <android-base/logging.h>
20 #include <thread>
21 
22 namespace aidl {
23 namespace android {
24 namespace hardware {
25 namespace vibrator {
26 
27 static constexpr int32_t kComposeDelayMaxMs = 1000;
28 static constexpr int32_t kComposeSizeMax = 256;
29 
getCapabilities(int32_t * _aidl_return)30 ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
31     LOG(INFO) << "Vibrator reporting capabilities";
32     *_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
33                     IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
34                     IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
35                     IVibrator::CAP_ALWAYS_ON_CONTROL;
36     return ndk::ScopedAStatus::ok();
37 }
38 
off()39 ndk::ScopedAStatus Vibrator::off() {
40     LOG(INFO) << "Vibrator off";
41     return ndk::ScopedAStatus::ok();
42 }
43 
on(int32_t timeoutMs,const std::shared_ptr<IVibratorCallback> & callback)44 ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
45                                 const std::shared_ptr<IVibratorCallback>& callback) {
46     LOG(INFO) << "Vibrator on for timeoutMs: " << timeoutMs;
47     if (callback != nullptr) {
48         std::thread([=] {
49             LOG(INFO) << "Starting on on another thread";
50             usleep(timeoutMs * 1000);
51             LOG(INFO) << "Notifying on complete";
52             if (!callback->onComplete().isOk()) {
53                 LOG(ERROR) << "Failed to call onComplete";
54             }
55         }).detach();
56     }
57     return ndk::ScopedAStatus::ok();
58 }
59 
perform(Effect effect,EffectStrength strength,const std::shared_ptr<IVibratorCallback> & callback,int32_t * _aidl_return)60 ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
61                                      const std::shared_ptr<IVibratorCallback>& callback,
62                                      int32_t* _aidl_return) {
63     LOG(INFO) << "Vibrator perform";
64 
65     if (effect != Effect::CLICK && effect != Effect::TICK) {
66         return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
67     }
68     if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
69         strength != EffectStrength::STRONG) {
70         return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
71     }
72 
73     constexpr size_t kEffectMillis = 100;
74 
75     if (callback != nullptr) {
76         std::thread([=] {
77             LOG(INFO) << "Starting perform on another thread";
78             usleep(kEffectMillis * 1000);
79             LOG(INFO) << "Notifying perform complete";
80             callback->onComplete();
81         }).detach();
82     }
83 
84     *_aidl_return = kEffectMillis;
85     return ndk::ScopedAStatus::ok();
86 }
87 
getSupportedEffects(std::vector<Effect> * _aidl_return)88 ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
89     *_aidl_return = {Effect::CLICK, Effect::TICK};
90     return ndk::ScopedAStatus::ok();
91 }
92 
setAmplitude(float amplitude)93 ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
94     LOG(INFO) << "Vibrator set amplitude: " << amplitude;
95     if (amplitude <= 0.0f || amplitude > 1.0f) {
96         return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
97     }
98     return ndk::ScopedAStatus::ok();
99 }
100 
setExternalControl(bool enabled)101 ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
102     LOG(INFO) << "Vibrator set external control: " << enabled;
103     return ndk::ScopedAStatus::ok();
104 }
105 
getCompositionDelayMax(int32_t * maxDelayMs)106 ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
107     *maxDelayMs = kComposeDelayMaxMs;
108     return ndk::ScopedAStatus::ok();
109 }
110 
getCompositionSizeMax(int32_t * maxSize)111 ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
112     *maxSize = kComposeSizeMax;
113     return ndk::ScopedAStatus::ok();
114 }
115 
getSupportedPrimitives(std::vector<CompositePrimitive> * supported)116 ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
117     *supported = {
118             CompositePrimitive::NOOP,       CompositePrimitive::CLICK,
119             CompositePrimitive::THUD,       CompositePrimitive::SPIN,
120             CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
121             CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
122     };
123     return ndk::ScopedAStatus::ok();
124 }
125 
getPrimitiveDuration(CompositePrimitive primitive,int32_t * durationMs)126 ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
127                                                   int32_t* durationMs) {
128     if (primitive != CompositePrimitive::NOOP) {
129         *durationMs = 100;
130     } else {
131         *durationMs = 0;
132     }
133     return ndk::ScopedAStatus::ok();
134 }
135 
compose(const std::vector<CompositeEffect> & composite,const std::shared_ptr<IVibratorCallback> & callback)136 ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
137                                      const std::shared_ptr<IVibratorCallback>& callback) {
138     if (composite.size() > kComposeSizeMax) {
139         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
140     }
141 
142     std::vector<CompositePrimitive> supported;
143     getSupportedPrimitives(&supported);
144 
145     for (auto& e : composite) {
146         if (e.delayMs > kComposeDelayMaxMs) {
147             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
148         }
149         if (e.scale <= 0.0f || e.scale > 1.0f) {
150             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
151         }
152         if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
153             return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
154         }
155     }
156 
157     std::thread([=] {
158         LOG(INFO) << "Starting compose on another thread";
159 
160         for (auto& e : composite) {
161             if (e.delayMs) {
162                 usleep(e.delayMs * 1000);
163             }
164             LOG(INFO) << "triggering primitive " << static_cast<int>(e.primitive) << " @ scale "
165                       << e.scale;
166 
167             int32_t durationMs;
168             getPrimitiveDuration(e.primitive, &durationMs);
169             usleep(durationMs * 1000);
170         }
171 
172         if (callback != nullptr) {
173             LOG(INFO) << "Notifying perform complete";
174             callback->onComplete();
175         }
176     }).detach();
177 
178     return ndk::ScopedAStatus::ok();
179 }
180 
getSupportedAlwaysOnEffects(std::vector<Effect> * _aidl_return)181 ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) {
182     return getSupportedEffects(_aidl_return);
183 }
184 
alwaysOnEnable(int32_t id,Effect effect,EffectStrength strength)185 ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
186     std::vector<Effect> effects;
187     getSupportedAlwaysOnEffects(&effects);
188 
189     if (std::find(effects.begin(), effects.end(), effect) == effects.end()) {
190         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
191     } else {
192         LOG(INFO) << "Enabling always-on ID " << id << " with " << toString(effect) << "/"
193                   << toString(strength);
194         return ndk::ScopedAStatus::ok();
195     }
196 }
197 
alwaysOnDisable(int32_t id)198 ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) {
199     LOG(INFO) << "Disabling always-on ID " << id;
200     return ndk::ScopedAStatus::ok();
201 }
202 
203 }  // namespace vibrator
204 }  // namespace hardware
205 }  // namespace android
206 }  // namespace aidl
207