Loading cmds/statsd/src/atoms.proto +25 −0 Original line number Diff line number Diff line Loading @@ -178,6 +178,7 @@ message Atom { BatteryVoltage battery_voltage = 10030; NumFingerprints num_fingerprints = 10031; ProcStats proc_stats = 10029; DiskIo disk_io = 10032; } // DO NOT USE field numbers above 100,000 in AOSP. Loading Loading @@ -2621,6 +2622,30 @@ message CategorySize { optional int64 cache_time_millis = 3; } /** * Pulls per uid I/O stats. The stats are cumulative since boot. * * Read/write bytes are I/O events from a storage device * Read/write chars are data requested by read/write syscalls, and can be * satisfied by caching. * * Pulled from StatsCompanionService, which reads proc/uid_io/stats. */ message DiskIo { optional int32 uid = 1 [(is_uid) = true]; optional int64 fg_chars_read = 2; optional int64 fg_chars_write = 3; optional int64 fg_bytes_read = 4; optional int64 fg_bytes_write = 5; optional int64 bg_chars_read = 6; optional int64 bg_chars_write = 7; optional int64 bg_bytes_read = 8; optional int64 bg_bytes_write = 9; optional int64 fg_fsync = 10; optional int64 bg_fsync= 11; } /** * Pulls the number of fingerprints for each user. * Loading cmds/statsd/src/external/StatsPullerManager.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,12 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // ProcStats. {android::util::PROC_STATS, {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}}, // Disk I/O stats per uid. {android::util::DISK_IO, {{2,3,4,5,6,7,8,9,10,11}, {}, 3 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_IO)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { Loading core/java/com/android/internal/os/StoragedUidIoStatsReader.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import android.os.StrictMode; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.nio.file.Files; /** * Reads /proc/uid_io/stats which has the line format: * * uid: foreground_read_chars foreground_write_chars foreground_read_bytes foreground_write_bytes * background_read_chars background_write_chars background_read_bytes background_write_bytes * foreground_fsync background_fsync * * This provides the number of bytes/chars read/written in foreground/background for each uid. * The file contains a monotonically increasing count of bytes/chars for a single boot. */ public class StoragedUidIoStatsReader { private static final String TAG = StoragedUidIoStatsReader.class.getSimpleName(); private static String sUidIoFile = "/proc/uid_io/stats"; public StoragedUidIoStatsReader() { } @VisibleForTesting public StoragedUidIoStatsReader(String file) { sUidIoFile = file; } /** * Notifies when new data is available. */ public interface Callback { /** * Provides data to the client. * * Note: Bytes are I/O events from a storage device. Chars are data requested by syscalls, * and can be satisfied by caching. */ void onUidStorageStats(int uid, long fgCharsRead, long fgCharsWrite, long fgBytesRead, long fgBytesWrite, long bgCharsRead, long bgCharsWrite, long bgBytesRead, long bgBytesWrite, long fgFsync, long bgFsync); } /** * Reads the proc file, calling into the callback with raw absolute value of I/O stats * for each UID. * * @param callback The callback to invoke for each line of the proc file. */ public void readAbsolute(Callback callback) { final int oldMask = StrictMode.allowThreadDiskReadsMask(); File file = new File(sUidIoFile); try (BufferedReader reader = Files.newBufferedReader(file.toPath())) { String line; while ((line = reader.readLine()) != null) { String[] fields = TextUtils.split(line, " "); if (fields.length != 11) { Slog.e(TAG, "Malformed entry in " + sUidIoFile + ": " + line); continue; } try { final String uidStr = fields[0]; final int uid = Integer.parseInt(fields[0], 10); final long fgCharsRead = Long.parseLong(fields[1], 10); final long fgCharsWrite = Long.parseLong(fields[2], 10); final long fgBytesRead = Long.parseLong(fields[3], 10); final long fgBytesWrite = Long.parseLong(fields[4], 10); final long bgCharsRead = Long.parseLong(fields[5], 10); final long bgCharsWrite = Long.parseLong(fields[6], 10); final long bgBytesRead = Long.parseLong(fields[7], 10); final long bgBytesWrite = Long.parseLong(fields[8], 10); final long fgFsync = Long.parseLong(fields[9], 10); final long bgFsync = Long.parseLong(fields[10], 10); callback.onUidStorageStats(uid, fgCharsRead, fgCharsWrite, fgBytesRead, fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite, fgFsync, bgFsync); } catch (NumberFormatException e) { Slog.e(TAG, "Could not parse entry in " + sUidIoFile + ": " + e.getMessage()); } } } catch (IOException e) { Slog.e(TAG, "Failed to read " + sUidIoFile + ": " + e.getMessage()); } finally { StrictMode.setThreadPolicyMask(oldMask); } } } core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,7 @@ import java.util.Random; /** * Test class for {@link KernelCpuProcReader}. * * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReader * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReaderTest */ @SmallTest @RunWith(AndroidJUnit4.class) Loading core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java 0 → 100644 +171 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import android.content.Context; import android.os.FileUtils; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.BufferedWriter; import java.io.File; import java.nio.file.Files; /** * Test class for {@link StoragedUidIoStatsReader}. * * To run it: * atest FrameworksCoreTests:com.android.internal.os.StoragedUidIoStatsReaderTest */ @SmallTest @RunWith(AndroidJUnit4.class) public class StoragedUidIoStatsReaderTest { private File mRoot; private File mTestDir; private File mTestFile; // private Random mRand = new Random(); private StoragedUidIoStatsReader mStoragedUidIoStatsReader; @Mock private StoragedUidIoStatsReader.Callback mCallback; private Context getContext() { return InstrumentationRegistry.getContext(); } @Before public void setUp() { MockitoAnnotations.initMocks(this); mTestDir = getContext().getDir("test", Context.MODE_PRIVATE); mRoot = getContext().getFilesDir(); mTestFile = new File(mTestDir, "test.file"); mStoragedUidIoStatsReader = new StoragedUidIoStatsReader(mTestFile.getAbsolutePath()); } @After public void tearDown() throws Exception { FileUtils.deleteContents(mTestDir); FileUtils.deleteContents(mRoot); } /** * Tests that reading will never call the callback. */ @Test public void testReadNonexistentFile() throws Exception { mStoragedUidIoStatsReader.readAbsolute(mCallback); verifyZeroInteractions(mCallback); } /** * Tests that reading a file with 3 uids works as expected. */ @Test public void testReadExpected() throws Exception { BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath()); int[] uids = {0, 100, 200}; long[] fg_chars_read = {1L, 101L, 201L}; long[] fg_chars_write = {2L, 102L, 202L}; long[] fg_bytes_read = {3L, 103L, 203L}; long[] fg_bytes_write = {4L, 104L, 204L}; long[] bg_chars_read = {5L, 105L, 205L}; long[] bg_chars_write = {6L, 106L, 206L}; long[] bg_bytes_read = {7L, 107L, 207L}; long[] bg_bytes_write = {8L, 108L, 208L}; long[] fg_fsync = {9L, 109L, 209L}; long[] bg_fsync = {10L, 110L, 210L}; for (int i = 0; i < uids.length; i++) { bufferedWriter.write(String .format("%d %d %d %d %d %d %d %d %d %d %d\n", uids[i], fg_chars_read[i], fg_chars_write[i], fg_bytes_read[i], fg_bytes_write[i], bg_chars_read[i], bg_chars_write[i], bg_bytes_read[i], bg_bytes_write[i], fg_fsync[i], bg_fsync[i])); } bufferedWriter.close(); mStoragedUidIoStatsReader.readAbsolute(mCallback); for (int i = 0; i < uids.length; i++) { verify(mCallback).onUidStorageStats(uids[i], fg_chars_read[i], fg_chars_write[i], fg_bytes_read[i], fg_bytes_write[i], bg_chars_read[i], bg_chars_write[i], bg_bytes_read[i], bg_bytes_write[i], fg_fsync[i], bg_fsync[i]); } verifyNoMoreInteractions(mCallback); } /** * Tests that a line with less than 11 items is passed over. */ @Test public void testLineDoesNotElevenEntries() throws Exception { BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath()); // Only has 10 numbers. bufferedWriter.write(String .format("%d %d %d %d %d %d %d %d %d %d\n", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); bufferedWriter.write(String .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)); bufferedWriter.close(); // Make sure we get the second line, but the first is skipped. mStoragedUidIoStatsReader.readAbsolute(mCallback); verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); verifyNoMoreInteractions(mCallback); } /** * Tests that a line that is malformed is passed over. */ @Test public void testLineIsMalformed() throws Exception { BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath()); // Line is not formatted properly. It has a string. bufferedWriter.write(String .format("%d %d %d %d %d %s %d %d %d %d %d\n", 0, 1, 2, 3, 4, "NotANumber", 5, 6, 7, 8, 9)); bufferedWriter.write(String .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)); bufferedWriter.close(); // Make sure we get the second line, but the first is skipped. mStoragedUidIoStatsReader.readAbsolute(mCallback); verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); verifyNoMoreInteractions(mCallback); } } Loading
cmds/statsd/src/atoms.proto +25 −0 Original line number Diff line number Diff line Loading @@ -178,6 +178,7 @@ message Atom { BatteryVoltage battery_voltage = 10030; NumFingerprints num_fingerprints = 10031; ProcStats proc_stats = 10029; DiskIo disk_io = 10032; } // DO NOT USE field numbers above 100,000 in AOSP. Loading Loading @@ -2621,6 +2622,30 @@ message CategorySize { optional int64 cache_time_millis = 3; } /** * Pulls per uid I/O stats. The stats are cumulative since boot. * * Read/write bytes are I/O events from a storage device * Read/write chars are data requested by read/write syscalls, and can be * satisfied by caching. * * Pulled from StatsCompanionService, which reads proc/uid_io/stats. */ message DiskIo { optional int32 uid = 1 [(is_uid) = true]; optional int64 fg_chars_read = 2; optional int64 fg_chars_write = 3; optional int64 fg_bytes_read = 4; optional int64 fg_bytes_write = 5; optional int64 bg_chars_read = 6; optional int64 bg_chars_write = 7; optional int64 bg_bytes_read = 8; optional int64 bg_bytes_write = 9; optional int64 fg_fsync = 10; optional int64 bg_fsync= 11; } /** * Pulls the number of fingerprints for each user. * Loading
cmds/statsd/src/external/StatsPullerManager.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,12 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // ProcStats. {android::util::PROC_STATS, {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}}, // Disk I/O stats per uid. {android::util::DISK_IO, {{2,3,4,5,6,7,8,9,10,11}, {}, 3 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_IO)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { Loading
core/java/com/android/internal/os/StoragedUidIoStatsReader.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import android.os.StrictMode; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.nio.file.Files; /** * Reads /proc/uid_io/stats which has the line format: * * uid: foreground_read_chars foreground_write_chars foreground_read_bytes foreground_write_bytes * background_read_chars background_write_chars background_read_bytes background_write_bytes * foreground_fsync background_fsync * * This provides the number of bytes/chars read/written in foreground/background for each uid. * The file contains a monotonically increasing count of bytes/chars for a single boot. */ public class StoragedUidIoStatsReader { private static final String TAG = StoragedUidIoStatsReader.class.getSimpleName(); private static String sUidIoFile = "/proc/uid_io/stats"; public StoragedUidIoStatsReader() { } @VisibleForTesting public StoragedUidIoStatsReader(String file) { sUidIoFile = file; } /** * Notifies when new data is available. */ public interface Callback { /** * Provides data to the client. * * Note: Bytes are I/O events from a storage device. Chars are data requested by syscalls, * and can be satisfied by caching. */ void onUidStorageStats(int uid, long fgCharsRead, long fgCharsWrite, long fgBytesRead, long fgBytesWrite, long bgCharsRead, long bgCharsWrite, long bgBytesRead, long bgBytesWrite, long fgFsync, long bgFsync); } /** * Reads the proc file, calling into the callback with raw absolute value of I/O stats * for each UID. * * @param callback The callback to invoke for each line of the proc file. */ public void readAbsolute(Callback callback) { final int oldMask = StrictMode.allowThreadDiskReadsMask(); File file = new File(sUidIoFile); try (BufferedReader reader = Files.newBufferedReader(file.toPath())) { String line; while ((line = reader.readLine()) != null) { String[] fields = TextUtils.split(line, " "); if (fields.length != 11) { Slog.e(TAG, "Malformed entry in " + sUidIoFile + ": " + line); continue; } try { final String uidStr = fields[0]; final int uid = Integer.parseInt(fields[0], 10); final long fgCharsRead = Long.parseLong(fields[1], 10); final long fgCharsWrite = Long.parseLong(fields[2], 10); final long fgBytesRead = Long.parseLong(fields[3], 10); final long fgBytesWrite = Long.parseLong(fields[4], 10); final long bgCharsRead = Long.parseLong(fields[5], 10); final long bgCharsWrite = Long.parseLong(fields[6], 10); final long bgBytesRead = Long.parseLong(fields[7], 10); final long bgBytesWrite = Long.parseLong(fields[8], 10); final long fgFsync = Long.parseLong(fields[9], 10); final long bgFsync = Long.parseLong(fields[10], 10); callback.onUidStorageStats(uid, fgCharsRead, fgCharsWrite, fgBytesRead, fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite, fgFsync, bgFsync); } catch (NumberFormatException e) { Slog.e(TAG, "Could not parse entry in " + sUidIoFile + ": " + e.getMessage()); } } } catch (IOException e) { Slog.e(TAG, "Failed to read " + sUidIoFile + ": " + e.getMessage()); } finally { StrictMode.setThreadPolicyMask(oldMask); } } }
core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,7 @@ import java.util.Random; /** * Test class for {@link KernelCpuProcReader}. * * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReader * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReaderTest */ @SmallTest @RunWith(AndroidJUnit4.class) Loading
core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java 0 → 100644 +171 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import android.content.Context; import android.os.FileUtils; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.BufferedWriter; import java.io.File; import java.nio.file.Files; /** * Test class for {@link StoragedUidIoStatsReader}. * * To run it: * atest FrameworksCoreTests:com.android.internal.os.StoragedUidIoStatsReaderTest */ @SmallTest @RunWith(AndroidJUnit4.class) public class StoragedUidIoStatsReaderTest { private File mRoot; private File mTestDir; private File mTestFile; // private Random mRand = new Random(); private StoragedUidIoStatsReader mStoragedUidIoStatsReader; @Mock private StoragedUidIoStatsReader.Callback mCallback; private Context getContext() { return InstrumentationRegistry.getContext(); } @Before public void setUp() { MockitoAnnotations.initMocks(this); mTestDir = getContext().getDir("test", Context.MODE_PRIVATE); mRoot = getContext().getFilesDir(); mTestFile = new File(mTestDir, "test.file"); mStoragedUidIoStatsReader = new StoragedUidIoStatsReader(mTestFile.getAbsolutePath()); } @After public void tearDown() throws Exception { FileUtils.deleteContents(mTestDir); FileUtils.deleteContents(mRoot); } /** * Tests that reading will never call the callback. */ @Test public void testReadNonexistentFile() throws Exception { mStoragedUidIoStatsReader.readAbsolute(mCallback); verifyZeroInteractions(mCallback); } /** * Tests that reading a file with 3 uids works as expected. */ @Test public void testReadExpected() throws Exception { BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath()); int[] uids = {0, 100, 200}; long[] fg_chars_read = {1L, 101L, 201L}; long[] fg_chars_write = {2L, 102L, 202L}; long[] fg_bytes_read = {3L, 103L, 203L}; long[] fg_bytes_write = {4L, 104L, 204L}; long[] bg_chars_read = {5L, 105L, 205L}; long[] bg_chars_write = {6L, 106L, 206L}; long[] bg_bytes_read = {7L, 107L, 207L}; long[] bg_bytes_write = {8L, 108L, 208L}; long[] fg_fsync = {9L, 109L, 209L}; long[] bg_fsync = {10L, 110L, 210L}; for (int i = 0; i < uids.length; i++) { bufferedWriter.write(String .format("%d %d %d %d %d %d %d %d %d %d %d\n", uids[i], fg_chars_read[i], fg_chars_write[i], fg_bytes_read[i], fg_bytes_write[i], bg_chars_read[i], bg_chars_write[i], bg_bytes_read[i], bg_bytes_write[i], fg_fsync[i], bg_fsync[i])); } bufferedWriter.close(); mStoragedUidIoStatsReader.readAbsolute(mCallback); for (int i = 0; i < uids.length; i++) { verify(mCallback).onUidStorageStats(uids[i], fg_chars_read[i], fg_chars_write[i], fg_bytes_read[i], fg_bytes_write[i], bg_chars_read[i], bg_chars_write[i], bg_bytes_read[i], bg_bytes_write[i], fg_fsync[i], bg_fsync[i]); } verifyNoMoreInteractions(mCallback); } /** * Tests that a line with less than 11 items is passed over. */ @Test public void testLineDoesNotElevenEntries() throws Exception { BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath()); // Only has 10 numbers. bufferedWriter.write(String .format("%d %d %d %d %d %d %d %d %d %d\n", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); bufferedWriter.write(String .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)); bufferedWriter.close(); // Make sure we get the second line, but the first is skipped. mStoragedUidIoStatsReader.readAbsolute(mCallback); verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); verifyNoMoreInteractions(mCallback); } /** * Tests that a line that is malformed is passed over. */ @Test public void testLineIsMalformed() throws Exception { BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath()); // Line is not formatted properly. It has a string. bufferedWriter.write(String .format("%d %d %d %d %d %s %d %d %d %d %d\n", 0, 1, 2, 3, 4, "NotANumber", 5, 6, 7, 8, 9)); bufferedWriter.write(String .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)); bufferedWriter.close(); // Make sure we get the second line, but the first is skipped. mStoragedUidIoStatsReader.readAbsolute(mCallback); verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); verifyNoMoreInteractions(mCallback); } }