blob: c31737f329fc47d5feacb806340a3dc059f60b79 [file] [log] [blame]
Matt Mowere04eee72016-12-31 00:38:57 -06001/*
2 Copyright 2017 TeamWin
3 This file is part of TWRP/TeamWin Recovery Project.
4
5 TWRP is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 TWRP is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TWRP. If not, see <http://www.gnu.org/licenses/>.
17*/
18
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010019#include <stdarg.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <fcntl.h>
24#include <sys/types.h>
25#include <time.h>
26#include <unistd.h>
27#include <stdlib.h>
28
29#include <string>
Aleksa Saraib25a1832015-12-31 17:36:00 +010030#include <sstream>
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010031
32extern "C" {
33#include "../twcommon.h"
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010034}
Ethan Yonkerfbb43532015-12-28 21:54:50 +010035#include "../minuitwrp/minui.h"
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -050036#include "../twrp-functions.hpp"
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010037#include "rapidxml.hpp"
38#include "objects.hpp"
39
40GUIPatternPassword::GUIPatternPassword(xml_node<>* node)
41 : GUIObject(node)
42{
43 xml_attribute<>* attr;
44 xml_node<>* child;
45
Aleksa Saraib25a1832015-12-31 17:36:00 +010046 // 3x3 is the default.
47 mGridSize = 3;
48 mDots = new Dot[mGridSize * mGridSize];
49 mConnectedDots = new int[mGridSize * mGridSize];
50
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010051 ResetActiveDots();
52 mTrackingTouch = false;
53 mNeedRender = true;
54
55 ConvertStrToColor("blue", &mDotColor);
56 ConvertStrToColor("white", &mActiveDotColor);
57 ConvertStrToColor("blue", &mLineColor);
58
59 mDotImage = mActiveDotImage = NULL;
60 mDotCircle = mActiveDotCircle = NULL;
61 mDotRadius = 50;
62 mLineWidth = 35;
63
64 mAction = NULL;
65 mUpdate = 0;
66
67 if (!node)
68 return;
69
70 LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH, &mPlacement);
71
72 mAction = new GUIAction(node);
73
74 child = FindNode(node, "dot");
Matt Mowera8a89d12016-12-30 18:10:37 -060075 if (child)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010076 {
77 mDotColor = LoadAttrColor(child, "color", mDotColor);
78 mActiveDotColor = LoadAttrColor(child, "activecolor", mActiveDotColor);
79 mDotRadius = LoadAttrIntScaleX(child, "radius", mDotRadius);
80
81 mDotImage = LoadAttrImage(child, "image");
82 mActiveDotImage = LoadAttrImage(child, "activeimage");
83 }
84
85 child = FindNode(node, "line");
Matt Mowera8a89d12016-12-30 18:10:37 -060086 if (child)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010087 {
88 mLineColor = LoadAttrColor(child, "color", mLineColor);
89 mLineWidth = LoadAttrIntScaleX(child, "width", mLineWidth);
90 }
91
92 child = FindNode(node, "data");
Matt Mowera8a89d12016-12-30 18:10:37 -060093 if (child)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010094 mPassVar = LoadAttrString(child, "name", "");
95
Aleksa Saraib25a1832015-12-31 17:36:00 +010096 child = FindNode(node, "size");
Matt Mowera8a89d12016-12-30 18:10:37 -060097 if (child) {
Aleksa Saraib25a1832015-12-31 17:36:00 +010098 mSizeVar = LoadAttrString(child, "name", "");
99
100 // Use the configured default, if set.
101 size_t size = LoadAttrInt(child, "default", mGridSize);
102 Resize(size);
103 }
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100104
Matt Mowera8a89d12016-12-30 18:10:37 -0600105 if (!mDotImage || !mDotImage->GetResource() || !mActiveDotImage || !mActiveDotImage->GetResource())
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100106 {
107 mDotCircle = gr_render_circle(mDotRadius, mDotColor.red, mDotColor.green, mDotColor.blue, mDotColor.alpha);
108 mActiveDotCircle = gr_render_circle(mDotRadius/2, mActiveDotColor.red, mActiveDotColor.green, mActiveDotColor.blue, mActiveDotColor.alpha);
109 }
110 else
111 mDotRadius = mDotImage->GetWidth()/2;
112
113 SetRenderPos(mRenderX, mRenderY, mRenderW, mRenderH);
114}
115
116GUIPatternPassword::~GUIPatternPassword()
117{
118 delete mDotImage;
119 delete mActiveDotImage;
120 delete mAction;
121
Aleksa Saraib25a1832015-12-31 17:36:00 +0100122 delete[] mDots;
123 delete[] mConnectedDots;
124
Matt Mowera8a89d12016-12-30 18:10:37 -0600125 if (mDotCircle)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100126 gr_free_surface(mDotCircle);
127
Matt Mowera8a89d12016-12-30 18:10:37 -0600128 if (mActiveDotCircle)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100129 gr_free_surface(mActiveDotCircle);
130}
131
132void GUIPatternPassword::ResetActiveDots()
133{
134 mConnectedDotsLen = 0;
135 mCurLineX = mCurLineY = -1;
Matt Mowera8a89d12016-12-30 18:10:37 -0600136 for (size_t i = 0; i < mGridSize * mGridSize; ++i)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100137 mDots[i].active = false;
138}
139
140int GUIPatternPassword::SetRenderPos(int x, int y, int w, int h)
141{
142 mRenderX = x;
143 mRenderY = y;
144
145 if (w || h)
146 {
147 mRenderW = w;
148 mRenderH = h;
149
150 mAction->SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
151 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
152 }
153
154 CalculateDotPositions();
155 return 0;
156}
157
158void GUIPatternPassword::CalculateDotPositions(void)
159{
Aleksa Saraib25a1832015-12-31 17:36:00 +0100160 const int num_gaps = mGridSize - 1;
161 const int step_x = (mRenderW - mDotRadius*2) / num_gaps;
162 const int step_y = (mRenderH - mDotRadius*2) / num_gaps;
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100163 int x = mRenderX;
164 int y = mRenderY;
165
Aleksa Saraib25a1832015-12-31 17:36:00 +0100166 /* Order is important for keyphrase generation:
167 *
168 * 0 1 2 3 ... n-1
169 * n n+1 n+2 n+3 ... 2n-1
170 * 2n 2n+1 2n+2 2n+3 ... 3n-1
171 * 3n 3n+1 3n+2 3n+3 ... 4n-1
172 * : : : :
173 * n*n-1
174 */
175
Matt Mowera8a89d12016-12-30 18:10:37 -0600176 for (size_t r = 0; r < mGridSize; ++r)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100177 {
Matt Mowera8a89d12016-12-30 18:10:37 -0600178 for (size_t c = 0; c < mGridSize; ++c)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100179 {
Aleksa Saraib25a1832015-12-31 17:36:00 +0100180 mDots[mGridSize*r + c].x = x;
181 mDots[mGridSize*r + c].y = y;
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100182 x += step_x;
183 }
184 x = mRenderX;
185 y += step_y;
186 }
187}
188
189int GUIPatternPassword::Render(void)
190{
Matt Mowera8a89d12016-12-30 18:10:37 -0600191 if (!isConditionTrue())
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100192 return 0;
193
194 gr_color(mLineColor.red, mLineColor.green, mLineColor.blue, mLineColor.alpha);
Matt Mowera8a89d12016-12-30 18:10:37 -0600195 for (size_t i = 1; i < mConnectedDotsLen; ++i) {
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100196 const Dot& dp = mDots[mConnectedDots[i-1]];
197 const Dot& dc = mDots[mConnectedDots[i]];
198 gr_line(dp.x + mDotRadius, dp.y + mDotRadius, dc.x + mDotRadius, dc.y + mDotRadius, mLineWidth);
199 }
200
Matt Mowera8a89d12016-12-30 18:10:37 -0600201 if (mConnectedDotsLen > 0 && mTrackingTouch) {
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100202 const Dot& dc = mDots[mConnectedDots[mConnectedDotsLen-1]];
203 gr_line(dc.x + mDotRadius, dc.y + mDotRadius, mCurLineX, mCurLineY, mLineWidth);
204 }
205
Matt Mowera8a89d12016-12-30 18:10:37 -0600206 for (size_t i = 0; i < mGridSize * mGridSize; ++i) {
207 if (mDotCircle) {
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100208 gr_blit(mDotCircle, 0, 0, gr_get_width(mDotCircle), gr_get_height(mDotCircle), mDots[i].x, mDots[i].y);
Matt Mowera8a89d12016-12-30 18:10:37 -0600209 if (mDots[i].active) {
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100210 gr_blit(mActiveDotCircle, 0, 0, gr_get_width(mActiveDotCircle), gr_get_height(mActiveDotCircle), mDots[i].x + mDotRadius/2, mDots[i].y + mDotRadius/2);
211 }
212 } else {
Matt Mowera8a89d12016-12-30 18:10:37 -0600213 if (mDots[i].active) {
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100214 gr_blit(mActiveDotImage->GetResource(), 0, 0, mActiveDotImage->GetWidth(), mActiveDotImage->GetHeight(),
215 mDots[i].x + (mDotRadius - mActiveDotImage->GetWidth()/2), mDots[i].y + (mDotRadius - mActiveDotImage->GetHeight()/2));
216 } else {
217 gr_blit(mDotImage->GetResource(), 0, 0, mDotImage->GetWidth(), mDotImage->GetHeight(), mDots[i].x, mDots[i].y);
218 }
219 }
220 }
221 return 0;
222}
223
224int GUIPatternPassword::Update(void)
225{
Matt Mowera8a89d12016-12-30 18:10:37 -0600226 if (!isConditionTrue())
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100227 return 0;
228
229 int res = mNeedRender ? 2 : 1;
230 mNeedRender = false;
231 return res;
232}
233
Aleksa Saraib25a1832015-12-31 17:36:00 +0100234void GUIPatternPassword::Resize(size_t n) {
Matt Mowera8a89d12016-12-30 18:10:37 -0600235 if (mGridSize == n)
Aleksa Saraib25a1832015-12-31 17:36:00 +0100236 return;
237
238 delete[] mDots;
239 delete[] mConnectedDots;
240
241 mGridSize = n;
242 mDots = new Dot[n*n];
243 mConnectedDots = new int[n*n];
244
245 ResetActiveDots();
246 CalculateDotPositions();
247 mTrackingTouch = false;
248 mNeedRender = true;
249}
250
251static int pow(int x, int i)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100252{
nailyk61c7c5c2016-08-19 13:36:07 +0200253 int result = 1;
254 if (i<0)
255 return 0;
256 while(i-- > 0)
257 result *= x;
258 return result;
Aleksa Saraib25a1832015-12-31 17:36:00 +0100259}
260
261static bool IsInCircle(int x, int y, int ox, int oy, int r)
262{
263 return pow(x - ox, 2) + pow(y - oy, 2) <= pow(r, 2);
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100264}
265
266int GUIPatternPassword::InDot(int x, int y)
267{
Matt Mowera8a89d12016-12-30 18:10:37 -0600268 for (size_t i = 0; i < mGridSize * mGridSize; ++i) {
269 if (IsInCircle(x, y, mDots[i].x + mDotRadius, mDots[i].y + mDotRadius, mDotRadius*3))
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100270 return i;
271 }
272 return -1;
273}
274
275bool GUIPatternPassword::DotUsed(int dot_idx)
276{
Matt Mowera8a89d12016-12-30 18:10:37 -0600277 for (size_t i = 0; i < mConnectedDotsLen; ++i) {
278 if (mConnectedDots[i] == dot_idx)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100279 return true;
280 }
281 return false;
282}
283
284void GUIPatternPassword::ConnectDot(int dot_idx)
285{
Matt Mowera8a89d12016-12-30 18:10:37 -0600286 if (mConnectedDotsLen >= mGridSize * mGridSize)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100287 {
288 LOGERR("mConnectedDots in GUIPatternPassword has overflown!\n");
289 return;
290 }
291
292 mConnectedDots[mConnectedDotsLen++] = dot_idx;
293 mDots[dot_idx].active = true;
294}
295
Aleksa Saraib25a1832015-12-31 17:36:00 +0100296void GUIPatternPassword::ConnectIntermediateDots(int next_dot_idx)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100297{
Matt Mowera8a89d12016-12-30 18:10:37 -0600298 if (mConnectedDotsLen == 0)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100299 return;
300
Aleksa Saraib25a1832015-12-31 17:36:00 +0100301 const int prev_dot_idx = mConnectedDots[mConnectedDotsLen-1];
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100302
Aleksa Saraib25a1832015-12-31 17:36:00 +0100303 int px = prev_dot_idx % mGridSize;
304 int py = prev_dot_idx / mGridSize;
305
306 int nx = next_dot_idx % mGridSize;
307 int ny = next_dot_idx / mGridSize;
308
309 /*
310 * We connect all dots that are in a straight line between the previous dot
311 * and the next one. This is simple for 3x3, but is more complicated for
312 * larger grids.
313 *
314 * Weirdly, Android doesn't do the logical thing when it comes to connecting
315 * dots between two points. Rather than simply adding all points that lie
316 * on the line between the start and end points, it instead only connects
317 * dots that are adjacent in only three directions -- horizontal, vertical
318 * and diagonal (45°).
319 *
320 * So we can just iterate over the correct axes, taking care to ensure that
321 * the order in which the intermediate points are added to the pattern is
322 * correct.
323 */
324
325 int x = px;
326 int y = py;
327
328 int Dx = (nx > px) ? 1 : -1;
329 int Dy = (ny > py) ? 1 : -1;
330
331 // Vertical lines.
Matt Mowera8a89d12016-12-30 18:10:37 -0600332 if (px == nx)
Aleksa Saraib25a1832015-12-31 17:36:00 +0100333 Dx = 0;
334
335 // Horizontal lines.
Matt Mowera8a89d12016-12-30 18:10:37 -0600336 else if (py == ny)
Aleksa Saraib25a1832015-12-31 17:36:00 +0100337 Dy = 0;
338
339 // Diagonal lines (|∆x| = |∆y|).
Matt Mowera8a89d12016-12-30 18:10:37 -0600340 else if (abs(px - nx) == abs(py - ny))
Aleksa Saraib25a1832015-12-31 17:36:00 +0100341 ;
342
343 // No valid intermediate dots.
344 else
Vojtech Boceke8c39272015-03-15 15:25:37 +0100345 return;
Vojtech Boceke8c39272015-03-15 15:25:37 +0100346
Aleksa Saraib25a1832015-12-31 17:36:00 +0100347 // Iterate along axis, adding dots in the correct order.
Matt Mowera8a89d12016-12-30 18:10:37 -0600348 while ((Dy == 0 || y != ny - Dy) && (Dx == 0 || x != nx - Dx)) {
Aleksa Saraib25a1832015-12-31 17:36:00 +0100349 x += Dx;
350 y += Dy;
351
352 int idx = mGridSize * y + x;
Matt Mowera8a89d12016-12-30 18:10:37 -0600353 if (!DotUsed(idx))
Aleksa Saraib25a1832015-12-31 17:36:00 +0100354 ConnectDot(idx);
355 }
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100356}
357
358int GUIPatternPassword::NotifyTouch(TOUCH_STATE state, int x, int y)
359{
Matt Mowera8a89d12016-12-30 18:10:37 -0600360 if (!isConditionTrue())
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100361 return -1;
362
363 switch (state)
364 {
365 case TOUCH_START:
366 {
367 const int dot_idx = InDot(x, y);
Matt Mowera8a89d12016-12-30 18:10:37 -0600368 if (dot_idx == -1)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100369 break;
370
371 mTrackingTouch = true;
372 ResetActiveDots();
373 ConnectDot(dot_idx);
374 DataManager::Vibrate("tw_button_vibrate");
375 mCurLineX = x;
376 mCurLineY = y;
377 mNeedRender = true;
378 break;
379 }
380 case TOUCH_DRAG:
381 {
Matt Mowera8a89d12016-12-30 18:10:37 -0600382 if (!mTrackingTouch)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100383 break;
384
385 const int dot_idx = InDot(x, y);
Matt Mowera8a89d12016-12-30 18:10:37 -0600386 if (dot_idx != -1 && !DotUsed(dot_idx))
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100387 {
388 ConnectIntermediateDots(dot_idx);
389 ConnectDot(dot_idx);
390 DataManager::Vibrate("tw_button_vibrate");
391 }
392
393 mCurLineX = x;
394 mCurLineY = y;
395 mNeedRender = true;
396 break;
397 }
398 case TOUCH_RELEASE:
399 {
Matt Mowera8a89d12016-12-30 18:10:37 -0600400 if (!mTrackingTouch)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100401 break;
402
403 mNeedRender = true;
404 mTrackingTouch = false;
405 PatternDrawn();
406 ResetActiveDots();
407 break;
408 }
409 default:
410 break;
411 }
412 return 0;
413}
414
Aleksa Saraib25a1832015-12-31 17:36:00 +0100415int GUIPatternPassword::NotifyVarChange(const std::string& varName, const std::string& value)
416{
Matt Mowera8a89d12016-12-30 18:10:37 -0600417 if (!isConditionTrue())
Aleksa Saraib25a1832015-12-31 17:36:00 +0100418 return 0;
419
Matt Mowera8a89d12016-12-30 18:10:37 -0600420 if (varName == mSizeVar) {
Aleksa Saraib25a1832015-12-31 17:36:00 +0100421 Resize(atoi(value.c_str()));
422 mUpdate = true;
423 }
424 return 0;
425}
426
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500427static unsigned int getSDKVersion(void) {
428 unsigned int sdkver = 23;
429 string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
430 if (!sdkverstr.empty()) {
431 sdkver = (unsigned int)strtoull(sdkverstr.c_str(), NULL, 10);
432 sdkver = (sdkver != 0) ? sdkver : 23;
433 }
434 LOGINFO("sdk version is %u\n", sdkver);
435 return sdkver;
436}
437
Aleksa Saraib25a1832015-12-31 17:36:00 +0100438std::string GUIPatternPassword::GeneratePassphrase()
439{
440 char pattern[mConnectedDotsLen];
Matt Mowera8a89d12016-12-30 18:10:37 -0600441 for (size_t i = 0; i < mConnectedDotsLen; i++) {
442 pattern[i] = (char) mConnectedDots[i];
Aleksa Saraib25a1832015-12-31 17:36:00 +0100443 }
444
445 std::stringstream pass;
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500446 char buffer[3] = {0};
Aleksa Saraib25a1832015-12-31 17:36:00 +0100447
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500448 if ((mGridSize == 3) || (getSDKVersion() >= 23)) {
449 // Marshmallow uses a consistent method
450 for (size_t i = 0; i < mConnectedDotsLen; i++) {
451 buffer[0] = (pattern[i] & 0xff) + '1';
452 pass << std::string(buffer);
453 }
454 } else {
Aleksa Saraib25a1832015-12-31 17:36:00 +0100455 /*
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500456 * Okay, rant time for pre-Marshmallow ROMs.
Aleksa Saraib25a1832015-12-31 17:36:00 +0100457 * It turns out that Android and CyanogenMod have *two* separate methods
458 * for generating passphrases from patterns. This is a legacy issue, as
459 * Android only supports 3x3 grids, and so we need to support both.
460 * Luckily, CyanogenMod is in the same boat as us and needs to support
461 * Android's 3x3 encryption style.
462 *
463 * In order to generate a 3x3 passphrase, add 1 to each dot index
464 * and concatenate the string representation of the integers. No
465 * padding should be added.
466 *
467 * For *all* other NxN passphrases (until a 16x16 grid comes along),
468 * they are generated by taking "%.2x" for each dot index and
469 * concatenating the results (without adding 1).
470 */
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500471 for (size_t i = 0; i < mConnectedDotsLen; i++) {
472 snprintf(buffer, 3, "%.2x", pattern[i] & 0xff);
Aleksa Saraib25a1832015-12-31 17:36:00 +0100473 pass << std::string(buffer);
474 }
475 }
476
477 return pass.str();
478}
479
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100480void GUIPatternPassword::PatternDrawn()
481{
Matt Mowera8a89d12016-12-30 18:10:37 -0600482 if (!mPassVar.empty() && mConnectedDotsLen > 0)
Aleksa Saraib25a1832015-12-31 17:36:00 +0100483 DataManager::SetValue(mPassVar, GeneratePassphrase());
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100484
Matt Mowera8a89d12016-12-30 18:10:37 -0600485 if (mAction)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100486 mAction->doActions();
487}