• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 // Unit Test for MediaTranscodingService.
18 
19 //#define LOG_NDEBUG 0
20 #define LOG_TAG "MediaTranscodingServiceResourceTest"
21 
22 #include <aidl/android/media/BnResourceManagerClient.h>
23 #include <aidl/android/media/IResourceManagerService.h>
24 #include <binder/ActivityManager.h>
25 
26 #include "MediaTranscodingServiceTestHelper.h"
27 
28 /*
29  * Tests media transcoding service with real transcoder.
30  *
31  * Uses the same test assets as the MediaTranscoder unit tests. Before running the test,
32  * please make sure to push the test assets to /sdcard:
33  *
34  * adb push $TOP/frameworks/av/media/libmediatranscoding/transcoder/tests/assets /data/local/tmp/TranscodingTestAssets
35  */
36 namespace android {
37 
38 namespace media {
39 
40 constexpr int64_t kPaddingUs = 400000;
41 constexpr int32_t kBitRate = 8 * 1000 * 1000;  // 8Mbs
42 
43 constexpr const char* kLongSrcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
44 
45 constexpr const char* kResourcePolicyTestActivity =
46         "/com.android.tests.transcoding.ResourcePolicyTestActivity";
47 
48 #define OUTPATH(name) "/data/local/tmp/MediaTranscodingService_" #name ".MP4"
49 
50 /*
51  * The OOM score we're going to ask ResourceManager to use for our native transcoding
52  * service. ResourceManager issues reclaims based on these scores. It gets the scores
53  * from ActivityManagerService, which doesn't track native services. The values of the
54  * OOM scores are defined in:
55  * frameworks/base/services/core/java/com/android/server/am/ProcessList.java
56  * We use SERVICE_ADJ which is lower priority than an app possibly visible to the
57  * user, but higher priority than a cached app (which could be killed without disruption
58  * to the user).
59  */
60 constexpr static int32_t SERVICE_ADJ = 500;
61 
62 using Status = ::ndk::ScopedAStatus;
63 using aidl::android::media::BnResourceManagerClient;
64 using aidl::android::media::IResourceManagerService;
65 
66 /*
67  * Placeholder ResourceManagerClient for registering process info override
68  * with the IResourceManagerService. This is only used as a token by the service
69  * to get notifications about binder death, not used for reclaiming resources.
70  */
71 struct ResourceManagerClient : public BnResourceManagerClient {
72     explicit ResourceManagerClient() = default;
73 
reclaimResourceandroid::media::ResourceManagerClient74     Status reclaimResource(bool* _aidl_return) override {
75         *_aidl_return = false;
76         return Status::ok();
77     }
78 
getNameandroid::media::ResourceManagerClient79     Status getName(::std::string* _aidl_return) override {
80         _aidl_return->clear();
81         return Status::ok();
82     }
83 
84     virtual ~ResourceManagerClient() = default;
85 };
86 
87 static std::shared_ptr<ResourceManagerClient> gResourceManagerClient =
88         ::ndk::SharedRefBase::make<ResourceManagerClient>();
89 
TranscodingHelper_setProcessInfoOverride(int32_t procState,int32_t oomScore)90 void TranscodingHelper_setProcessInfoOverride(int32_t procState, int32_t oomScore) {
91     ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_manager"));
92     std::shared_ptr<IResourceManagerService> service = IResourceManagerService::fromBinder(binder);
93     if (service == nullptr) {
94         ALOGE("Failed to get IResourceManagerService");
95         return;
96     }
97     Status status =
98             service->overrideProcessInfo(gResourceManagerClient, getpid(), procState, oomScore);
99     if (!status.isOk()) {
100         ALOGW("Failed to setProcessInfoOverride.");
101     }
102 }
103 
104 class MediaTranscodingServiceResourceTest : public MediaTranscodingServiceTestBase {
105 public:
MediaTranscodingServiceResourceTest()106     MediaTranscodingServiceResourceTest() { ALOGI("MediaTranscodingServiceResourceTest created"); }
107 
~MediaTranscodingServiceResourceTest()108     virtual ~MediaTranscodingServiceResourceTest() {
109         ALOGI("MediaTranscodingServiceResourceTest destroyed");
110     }
111 };
112 
113 /**
114  * Basic testing for handling resource lost.
115  *
116  * This test starts a transcoding session (that's somewhat long and takes several seconds),
117  * then launches an activity that allocates video codec instances until it hits insufficient
118  * resource error. Because the activity is running in foreground,
119  * ResourceManager would reclaim codecs from transcoding service which should
120  * cause the session to be paused. The activity will hold the codecs for a few seconds
121  * before releasing them, and the transcoding service should be able to resume
122  * and complete the session.
123  *
124  * Note that this test must run as root. We need to simulate submitting a request for a
125  * client {uid,pid} running at lower priority. As a cmd line test, it's not easy to get the
126  * pid of a living app, so we use our own {uid,pid} to submit. However, since we're a native
127  * process, RM doesn't have our proc info and the reclaim will fail. So we need to use
128  * RM's setProcessInfoOverride to override our proc info, which requires permission (unless root).
129  */
TEST_F(MediaTranscodingServiceResourceTest,TestResourceLost)130 TEST_F(MediaTranscodingServiceResourceTest, TestResourceLost) {
131     ALOGD("TestResourceLost starting..., pid %d", ::getpid());
132 
133     // We're going to submit the request using our own {uid,pid}. Since we're a native
134     // process, RM doesn't have our proc info and the reclaim will fail. So we need to use
135     // RM's setProcessInfoOverride to override our proc info.
136     TranscodingHelper_setProcessInfoOverride(ActivityManager::PROCESS_STATE_SERVICE, SERVICE_ADJ);
137 
138     EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
139     EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
140     EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
141 
142     registerMultipleClients();
143 
144     const char* srcPath0 = kLongSrcPath;
145     const char* dstPath0 = OUTPATH(TestPauseResumeMultiClients_Client0);
146     deleteFile(dstPath0);
147 
148     ALOGD("Moving app A to top...");
149     EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
150 
151     // Submit session to Client1.
152     ALOGD("Submitting session to client1 (app A) ...");
153     EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal,
154                                  kBitRate, ::getpid(), ::getuid()));
155 
156     // Client1's session should start immediately.
157     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
158 
159     // Launch ResourcePolicyTestActivity, which will try to allocate up to 32
160     // instances, which should trigger insufficient resources on most devices.
161     // (Note that it's possible that the device supports a very high number of
162     // resource instances, in which case we'll simply require that the session completes.)
163     ALOGD("Launch ResourcePolicyTestActivity...");
164     EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kResourcePolicyTestActivity));
165 
166     // The basic requirement is that the session should complete. Wait for finish
167     // event to come and pop up all events received.
168     std::list<EventTracker::Event> events;
169     EXPECT_TRUE(mClient1->waitForSpecificEventAndPop(EventTracker::Finished(CLIENT(1), 0), &events,
170                                                      15000000));
171 
172     // If there is only 1 event, it must be finish (otherwise waitForSpecificEventAndPop
173     // woudldn't pop up anything), and we're ok.
174     //
175     // TODO: If there is only 1 event (finish), and no pause/resume happened, we need
176     // to verify that the ResourcePolicyTestActivity actually was able to allocate
177     // all 32 instances without hitting insufficient resources. Otherwise, it could
178     // be that ResourceManager was not able to reclaim codecs from the transcoding
179     // service at all, which means the resource management is broken.
180     if (events.size() > 1) {
181         EXPECT_TRUE(events.size() >= 3);
182         size_t i = 0;
183         for (auto& event : events) {
184             if (i == 0) {
185                 EXPECT_EQ(event, EventTracker::Pause(CLIENT(1), 0));
186             } else if (i == events.size() - 2) {
187                 EXPECT_EQ(event, EventTracker::Resume(CLIENT(1), 0));
188             } else if (i == events.size() - 1) {
189                 EXPECT_EQ(event, EventTracker::Finished(CLIENT(1), 0));
190             } else {
191                 EXPECT_TRUE(event == EventTracker::Pause(CLIENT(1), 0) ||
192                             event == EventTracker::Resume(CLIENT(1), 0));
193             }
194             i++;
195         }
196     }
197 
198     unregisterMultipleClients();
199 
200     EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
201 }
202 
203 }  // namespace media
204 }  // namespace android
205