Merge "DefaultNetworkEvent metrics: rehaul" am: addecd1491
am: d025358166 Change-Id: If6bf966ba547a2ce50e17340e0374dd9b75b80d9
This commit is contained in:
@@ -2095,9 +2095,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
final boolean valid =
|
final boolean valid =
|
||||||
(msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
|
(msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
|
||||||
final boolean wasValidated = nai.lastValidated;
|
final boolean wasValidated = nai.lastValidated;
|
||||||
|
final boolean wasDefault = isDefaultNetwork(nai);
|
||||||
if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
|
if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
|
||||||
(msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
|
(msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
|
||||||
if (valid != nai.lastValidated) {
|
if (valid != nai.lastValidated) {
|
||||||
|
if (wasDefault) {
|
||||||
|
metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
|
||||||
|
SystemClock.elapsedRealtime(), valid);
|
||||||
|
}
|
||||||
final int oldScore = nai.getCurrentScore();
|
final int oldScore = nai.getCurrentScore();
|
||||||
nai.lastValidated = valid;
|
nai.lastValidated = valid;
|
||||||
nai.everValidated |= valid;
|
nai.everValidated |= valid;
|
||||||
@@ -2269,7 +2274,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// Let rematchAllNetworksAndRequests() below record a new default network event
|
// Let rematchAllNetworksAndRequests() below record a new default network event
|
||||||
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
|
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
|
||||||
// whose timestamps tell how long it takes to recover a default network.
|
// whose timestamps tell how long it takes to recover a default network.
|
||||||
metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(null, nai);
|
long now = SystemClock.elapsedRealtime();
|
||||||
|
metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
|
||||||
}
|
}
|
||||||
notifyIfacesChangedForNetworkStats();
|
notifyIfacesChangedForNetworkStats();
|
||||||
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
|
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
|
||||||
@@ -5024,7 +5030,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
makeDefault(newNetwork);
|
makeDefault(newNetwork);
|
||||||
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
|
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
|
||||||
metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
|
metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
|
||||||
newNetwork, oldDefaultNetwork);
|
now, newNetwork, oldDefaultNetwork);
|
||||||
// Have a new default network, release the transition wakelock in
|
// Have a new default network, release the transition wakelock in
|
||||||
scheduleReleaseNetworkTransitionWakelock();
|
scheduleReleaseNetworkTransitionWakelock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,37 +198,33 @@ public class IpConnectivityEventBuilderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultNetworkEventSerialization() {
|
public void testDefaultNetworkEventSerialization() {
|
||||||
DefaultNetworkEvent ev = new DefaultNetworkEvent();
|
DefaultNetworkEvent ev = new DefaultNetworkEvent(1001);
|
||||||
ev.netId = 102;
|
ev.netId = 102;
|
||||||
ev.prevNetId = 101;
|
ev.transports = 2;
|
||||||
ev.transportTypes = new int[]{1, 2, 3};
|
ev.previousTransports = 4;
|
||||||
ev.prevIPv4 = true;
|
ev.ipv4 = true;
|
||||||
ev.prevIPv6 = true;
|
ev.initialScore = 20;
|
||||||
|
ev.finalScore = 60;
|
||||||
|
ev.durationMs = 54;
|
||||||
|
ev.validatedMs = 27;
|
||||||
|
|
||||||
String want = String.join("\n",
|
String want = String.join("\n",
|
||||||
"dropped_events: 0",
|
"dropped_events: 0",
|
||||||
"events <",
|
"events <",
|
||||||
" if_name: \"\"",
|
" if_name: \"\"",
|
||||||
" link_layer: 0",
|
" link_layer: 4",
|
||||||
" network_id: 102",
|
" network_id: 102",
|
||||||
" time_ms: 0",
|
" time_ms: 0",
|
||||||
" transports: 0",
|
" transports: 2",
|
||||||
" default_network_event <",
|
" default_network_event <",
|
||||||
" default_network_duration_ms: 0",
|
" default_network_duration_ms: 54",
|
||||||
" final_score: 0",
|
" final_score: 60",
|
||||||
" initial_score: 0",
|
" initial_score: 20",
|
||||||
" ip_support: 0",
|
" ip_support: 1",
|
||||||
" network_id <",
|
|
||||||
" network_id: 102",
|
|
||||||
" >",
|
|
||||||
" no_default_network_duration_ms: 0",
|
" no_default_network_duration_ms: 0",
|
||||||
" previous_network_id <",
|
" previous_default_network_link_layer: 1",
|
||||||
" network_id: 101",
|
" previous_network_ip_support: 0",
|
||||||
" >",
|
" validation_duration_ms: 27",
|
||||||
" previous_network_ip_support: 3",
|
|
||||||
" transport_types: 1",
|
|
||||||
" transport_types: 2",
|
|
||||||
" transport_types: 3",
|
|
||||||
" >",
|
" >",
|
||||||
">",
|
">",
|
||||||
"version: 2\n");
|
"version: 2\n");
|
||||||
|
|||||||
@@ -188,119 +188,99 @@ public class IpConnectivityMetricsTest {
|
|||||||
{makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)},
|
{makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
|
||||||
|
long durationMs = 1001;
|
||||||
for (NetworkAgentInfo[] pair : defaultNetworks) {
|
for (NetworkAgentInfo[] pair : defaultNetworks) {
|
||||||
mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(pair[1], pair[0]);
|
timeMs += durationMs;
|
||||||
|
durationMs += durationMs;
|
||||||
|
mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, pair[1], pair[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
String want = String.join("\n",
|
String want = String.join("\n",
|
||||||
"dropped_events: 0",
|
"dropped_events: 0",
|
||||||
"events <",
|
"events <",
|
||||||
" if_name: \"\"",
|
" if_name: \"\"",
|
||||||
" link_layer: 0",
|
" link_layer: 5",
|
||||||
" network_id: 100",
|
" network_id: 0",
|
||||||
" time_ms: 0",
|
" time_ms: 0",
|
||||||
" transports: 0",
|
" transports: 0",
|
||||||
" default_network_event <",
|
" default_network_event <",
|
||||||
" default_network_duration_ms: 0",
|
" default_network_duration_ms: 1001",
|
||||||
" final_score: 0",
|
" final_score: 0",
|
||||||
" initial_score: 0",
|
" initial_score: 0",
|
||||||
" ip_support: 0",
|
" ip_support: 0",
|
||||||
" network_id <",
|
|
||||||
" network_id: 100",
|
|
||||||
" >",
|
|
||||||
" no_default_network_duration_ms: 0",
|
" no_default_network_duration_ms: 0",
|
||||||
" previous_network_id <",
|
" previous_default_network_link_layer: 0",
|
||||||
" network_id: 0",
|
|
||||||
" >",
|
|
||||||
" previous_network_ip_support: 0",
|
" previous_network_ip_support: 0",
|
||||||
" transport_types: 0",
|
" validation_duration_ms: 0",
|
||||||
" >",
|
" >",
|
||||||
">",
|
">",
|
||||||
"events <",
|
"events <",
|
||||||
" if_name: \"\"",
|
" if_name: \"\"",
|
||||||
" link_layer: 0",
|
" link_layer: 2",
|
||||||
" network_id: 101",
|
|
||||||
" time_ms: 0",
|
|
||||||
" transports: 0",
|
|
||||||
" default_network_event <",
|
|
||||||
" default_network_duration_ms: 0",
|
|
||||||
" final_score: 0",
|
|
||||||
" initial_score: 0",
|
|
||||||
" ip_support: 0",
|
|
||||||
" network_id <",
|
|
||||||
" network_id: 101",
|
|
||||||
" >",
|
|
||||||
" no_default_network_duration_ms: 0",
|
|
||||||
" previous_network_id <",
|
|
||||||
" network_id: 100",
|
" network_id: 100",
|
||||||
" >",
|
|
||||||
" previous_network_ip_support: 3",
|
|
||||||
" transport_types: 1",
|
|
||||||
" >",
|
|
||||||
">",
|
|
||||||
"events <",
|
|
||||||
" if_name: \"\"",
|
|
||||||
" link_layer: 0",
|
|
||||||
" network_id: 0",
|
|
||||||
" time_ms: 0",
|
" time_ms: 0",
|
||||||
" transports: 0",
|
" transports: 1",
|
||||||
" default_network_event <",
|
" default_network_event <",
|
||||||
" default_network_duration_ms: 0",
|
" default_network_duration_ms: 2002",
|
||||||
" final_score: 0",
|
" final_score: 50",
|
||||||
" initial_score: 0",
|
" initial_score: 10",
|
||||||
" ip_support: 0",
|
" ip_support: 3",
|
||||||
" network_id <",
|
|
||||||
" network_id: 0",
|
|
||||||
" >",
|
|
||||||
" no_default_network_duration_ms: 0",
|
" no_default_network_duration_ms: 0",
|
||||||
" previous_network_id <",
|
" previous_default_network_link_layer: 0",
|
||||||
" network_id: 101",
|
|
||||||
" >",
|
|
||||||
" previous_network_ip_support: 1",
|
|
||||||
" >",
|
|
||||||
">",
|
|
||||||
"events <",
|
|
||||||
" if_name: \"\"",
|
|
||||||
" link_layer: 0",
|
|
||||||
" network_id: 102",
|
|
||||||
" time_ms: 0",
|
|
||||||
" transports: 0",
|
|
||||||
" default_network_event <",
|
|
||||||
" default_network_duration_ms: 0",
|
|
||||||
" final_score: 0",
|
|
||||||
" initial_score: 0",
|
|
||||||
" ip_support: 0",
|
|
||||||
" network_id <",
|
|
||||||
" network_id: 102",
|
|
||||||
" >",
|
|
||||||
" no_default_network_duration_ms: 0",
|
|
||||||
" previous_network_id <",
|
|
||||||
" network_id: 0",
|
|
||||||
" >",
|
|
||||||
" previous_network_ip_support: 0",
|
" previous_network_ip_support: 0",
|
||||||
" transport_types: 0",
|
" validation_duration_ms: 2002",
|
||||||
" >",
|
" >",
|
||||||
">",
|
">",
|
||||||
"events <",
|
"events <",
|
||||||
" if_name: \"\"",
|
" if_name: \"\"",
|
||||||
" link_layer: 0",
|
" link_layer: 4",
|
||||||
" network_id: 103",
|
" network_id: 101",
|
||||||
|
" time_ms: 0",
|
||||||
|
" transports: 2",
|
||||||
|
" default_network_event <",
|
||||||
|
" default_network_duration_ms: 4004",
|
||||||
|
" final_score: 60",
|
||||||
|
" initial_score: 20",
|
||||||
|
" ip_support: 1",
|
||||||
|
" no_default_network_duration_ms: 0",
|
||||||
|
" previous_default_network_link_layer: 2",
|
||||||
|
" previous_network_ip_support: 0",
|
||||||
|
" validation_duration_ms: 4004",
|
||||||
|
" >",
|
||||||
|
">",
|
||||||
|
"events <",
|
||||||
|
" if_name: \"\"",
|
||||||
|
" link_layer: 5",
|
||||||
|
" network_id: 0",
|
||||||
" time_ms: 0",
|
" time_ms: 0",
|
||||||
" transports: 0",
|
" transports: 0",
|
||||||
" default_network_event <",
|
" default_network_event <",
|
||||||
" default_network_duration_ms: 0",
|
" default_network_duration_ms: 8008",
|
||||||
" final_score: 0",
|
" final_score: 0",
|
||||||
" initial_score: 0",
|
" initial_score: 0",
|
||||||
" ip_support: 0",
|
" ip_support: 0",
|
||||||
" network_id <",
|
|
||||||
" network_id: 103",
|
|
||||||
" >",
|
|
||||||
" no_default_network_duration_ms: 0",
|
" no_default_network_duration_ms: 0",
|
||||||
" previous_network_id <",
|
" previous_default_network_link_layer: 4",
|
||||||
" network_id: 102",
|
" previous_network_ip_support: 0",
|
||||||
|
" validation_duration_ms: 0",
|
||||||
" >",
|
" >",
|
||||||
" previous_network_ip_support: 3",
|
">",
|
||||||
" transport_types: 1",
|
"events <",
|
||||||
|
" if_name: \"\"",
|
||||||
|
" link_layer: 2",
|
||||||
|
" network_id: 102",
|
||||||
|
" time_ms: 0",
|
||||||
|
" transports: 1",
|
||||||
|
" default_network_event <",
|
||||||
|
" default_network_duration_ms: 16016",
|
||||||
|
" final_score: 50",
|
||||||
|
" initial_score: 10",
|
||||||
|
" ip_support: 3",
|
||||||
|
" no_default_network_duration_ms: 0",
|
||||||
|
" previous_default_network_link_layer: 4",
|
||||||
|
" previous_network_ip_support: 0",
|
||||||
|
" validation_duration_ms: 16016",
|
||||||
" >",
|
" >",
|
||||||
">",
|
">",
|
||||||
"version: 2\n");
|
"version: 2\n");
|
||||||
@@ -379,12 +359,13 @@ public class IpConnectivityMetricsTest {
|
|||||||
wakeupEvent("wlan0", 10008);
|
wakeupEvent("wlan0", 10008);
|
||||||
wakeupEvent("rmnet0", 1000);
|
wakeupEvent("rmnet0", 1000);
|
||||||
|
|
||||||
|
long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
|
||||||
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
|
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
|
||||||
final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
|
final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
|
||||||
NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
|
NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
|
||||||
NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
|
NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
|
||||||
mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(cellNai, null);
|
mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 200, cellNai, null);
|
||||||
mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(wifiNai, cellNai);
|
mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 300, wifiNai, cellNai);
|
||||||
|
|
||||||
String want = String.join("\n",
|
String want = String.join("\n",
|
||||||
"dropped_events: 0",
|
"dropped_events: 0",
|
||||||
@@ -473,46 +454,36 @@ public class IpConnectivityMetricsTest {
|
|||||||
">",
|
">",
|
||||||
"events <",
|
"events <",
|
||||||
" if_name: \"\"",
|
" if_name: \"\"",
|
||||||
" link_layer: 0",
|
" link_layer: 5",
|
||||||
" network_id: 100",
|
" network_id: 0",
|
||||||
" time_ms: 0",
|
" time_ms: 0",
|
||||||
" transports: 0",
|
" transports: 0",
|
||||||
" default_network_event <",
|
" default_network_event <",
|
||||||
" default_network_duration_ms: 0",
|
" default_network_duration_ms: 200",
|
||||||
" final_score: 0",
|
" final_score: 0",
|
||||||
" initial_score: 0",
|
" initial_score: 0",
|
||||||
" ip_support: 0",
|
" ip_support: 0",
|
||||||
" network_id <",
|
|
||||||
" network_id: 100",
|
|
||||||
" >",
|
|
||||||
" no_default_network_duration_ms: 0",
|
" no_default_network_duration_ms: 0",
|
||||||
" previous_network_id <",
|
" previous_default_network_link_layer: 0",
|
||||||
" network_id: 0",
|
|
||||||
" >",
|
|
||||||
" previous_network_ip_support: 0",
|
" previous_network_ip_support: 0",
|
||||||
" transport_types: 0",
|
" validation_duration_ms: 0",
|
||||||
" >",
|
" >",
|
||||||
">",
|
">",
|
||||||
"events <",
|
"events <",
|
||||||
" if_name: \"\"",
|
" if_name: \"\"",
|
||||||
" link_layer: 0",
|
" link_layer: 2",
|
||||||
" network_id: 101",
|
|
||||||
" time_ms: 0",
|
|
||||||
" transports: 0",
|
|
||||||
" default_network_event <",
|
|
||||||
" default_network_duration_ms: 0",
|
|
||||||
" final_score: 0",
|
|
||||||
" initial_score: 0",
|
|
||||||
" ip_support: 0",
|
|
||||||
" network_id <",
|
|
||||||
" network_id: 101",
|
|
||||||
" >",
|
|
||||||
" no_default_network_duration_ms: 0",
|
|
||||||
" previous_network_id <",
|
|
||||||
" network_id: 100",
|
" network_id: 100",
|
||||||
" >",
|
" time_ms: 0",
|
||||||
" previous_network_ip_support: 2",
|
" transports: 1",
|
||||||
" transport_types: 1",
|
" default_network_event <",
|
||||||
|
" default_network_duration_ms: 100",
|
||||||
|
" final_score: 50",
|
||||||
|
" initial_score: 50",
|
||||||
|
" ip_support: 2",
|
||||||
|
" no_default_network_duration_ms: 0",
|
||||||
|
" previous_default_network_link_layer: 0",
|
||||||
|
" previous_network_ip_support: 0",
|
||||||
|
" validation_duration_ms: 100",
|
||||||
" >",
|
" >",
|
||||||
">",
|
">",
|
||||||
"events <",
|
"events <",
|
||||||
|
|||||||
@@ -82,9 +82,8 @@ public class NetdEventListenerServiceTest {
|
|||||||
public void testWakeupEventLogging() throws Exception {
|
public void testWakeupEventLogging() throws Exception {
|
||||||
final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
|
final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
|
||||||
|
|
||||||
// Assert no events
|
// Baseline without any event
|
||||||
String[] events1 = listNetdEvent();
|
String[] baseline = listNetdEvent();
|
||||||
assertEquals(new String[]{""}, events1);
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
String prefix = "iface:wlan0";
|
String prefix = "iface:wlan0";
|
||||||
@@ -93,7 +92,7 @@ public class NetdEventListenerServiceTest {
|
|||||||
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
|
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] events2 = listNetdEvent();
|
String[] events2 = remove(listNetdEvent(), baseline);
|
||||||
int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
|
int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
|
||||||
assertEquals(expectedLength2, events2.length);
|
assertEquals(expectedLength2, events2.length);
|
||||||
assertContains(events2[0], "WakeupStats");
|
assertContains(events2[0], "WakeupStats");
|
||||||
@@ -111,7 +110,7 @@ public class NetdEventListenerServiceTest {
|
|||||||
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
|
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] events3 = listNetdEvent();
|
String[] events3 = remove(listNetdEvent(), baseline);
|
||||||
int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
|
int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
|
||||||
assertEquals(expectedLength3, events3.length);
|
assertEquals(expectedLength3, events3.length);
|
||||||
assertContains(events2[0], "WakeupStats");
|
assertContains(events2[0], "WakeupStats");
|
||||||
@@ -126,7 +125,7 @@ public class NetdEventListenerServiceTest {
|
|||||||
uid = 45678;
|
uid = 45678;
|
||||||
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
|
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
|
||||||
|
|
||||||
String[] events4 = listNetdEvent();
|
String[] events4 = remove(listNetdEvent(), baseline);
|
||||||
String lastEvent = events4[events4.length - 1];
|
String lastEvent = events4[events4.length - 1];
|
||||||
assertContains(lastEvent, "WakeupEvent");
|
assertContains(lastEvent, "WakeupEvent");
|
||||||
assertContains(lastEvent, "wlan0");
|
assertContains(lastEvent, "wlan0");
|
||||||
@@ -423,7 +422,7 @@ public class NetdEventListenerServiceTest {
|
|||||||
final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
|
final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
while (System.currentTimeMillis() < stop) {
|
while (System.currentTimeMillis() < stop) {
|
||||||
mNetdEventListenerService.dump(pw);
|
mNetdEventListenerService.list(pw);
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
@@ -461,4 +460,16 @@ public class NetdEventListenerServiceTest {
|
|||||||
static void assertContains(String got, String want) {
|
static void assertContains(String got, String want) {
|
||||||
assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
|
assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static <T> T[] remove(T[] array, T[] filtered) {
|
||||||
|
List<T> c = Arrays.asList(filtered);
|
||||||
|
int next = 0;
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
if (c.contains(array[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
array[next++] = array[i];
|
||||||
|
}
|
||||||
|
return Arrays.copyOf(array, next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user